主要解決一下幾個問題:javascript
1.apply和call的區別在哪裏java
2.什麼狀況下用apply,什麼狀況下用call數組
3.apply的其餘巧妙用法(通常在什麼狀況下可使用apply)網絡
apply:方法能劫持另一個對象的方法,繼承另一個對象的屬性.app
Function.apply(obj,args)方法能接收兩個參數函數
obj:這個對象將代替Function類裏this對象測試
args:這個是數組,它將做爲參數傳給Function(args-->arguments)this
call:和apply的意思同樣,只不過是參數列表不同.spa
Function.call(obj,[param1[,param2[,…[,paramN]]]])prototype
obj:這個對象將代替Function類裏this對象
params:這個是一個參數列表
<script type="text/javascript"> /*定義一我的類*/ function Person(name,age) { this.name=name; this.age=age; } /*定義一個學生類*/ functionStudent(name,age,grade) { Person.apply(this,arguments); this.grade=grade; } //建立一個學生類 var student=new Student("zhangsan",21,"一年級"); //測試 alert("name:"+student.name+"\n"+"age:"+student.age+"\n"+"grade:"+student.grade); //你們能夠看到測試結果name:zhangsan age:21 grade:一年級 //學生類裏面我沒有給name和age屬性賦值啊,爲何又存在這兩個屬性的值呢,這個就是apply的神奇之處. </script>
分析: Person.apply(this,arguments);
this:在建立對象在這個時候表明的是student
arguments:是一個數組,也就是[「zhangsan」,」21」,」一年級」];
也就是通俗一點講就是:用student去執行Person這個類裏面的內容,在Person這個類裏面存在this.name等之類的語句,這樣就將屬性建立到了student對象裏面
在Studen函數裏面能夠將apply中修改爲以下:
Person.call(this,name,age);
這樣就ok了
在給對象參數的狀況下,若是參數的形式是數組的時候,好比apply示例裏面傳遞了參數arguments,這個參數是數組類型,而且在調用 Person的時候參數的列表是對應一致的(也就是Person和Student的參數列表前兩位是一致的) 就能夠採用 apply , 若是個人Person的參數列表是這樣的(age,name),而Student的參數列表是(name,age,grade),這樣就能夠用call來 實現了,也就是直接指定參數列表對應值的位置(Person.call(this,age,name,grade));
細心的人可能已經察覺到,在我調用apply方法的時候,第一個參數是對象(this), 第二個參數是一個數組集合, 在調用Person的時候,他須要的不是一個數組,可是爲何他給我一個數組我仍然能夠將數組解析爲一個一個的參數,這 個就是apply的一個巧妙的用處,能夠將一個數組默認的轉換爲一個參數列表([param1,param2,param3] 轉換爲 param1,param2,param3) 這個若是讓咱們用程序來實現將數組的每個項,來裝換爲參數的列表,可能都得費一會功夫,藉助apply的這點特性,因此就有了如下高效率的方法:
a)Math.max 能夠實現獲得數組中最大的一項
由於Math.max 參數裏面不支持Math.max([param1,param2]) 也就是數組
可是它支持Math.max(param1,param2,param3…),因此能夠根據剛纔apply的那個特色來解決 var max=Math.max.apply(null,array),這樣輕易的能夠獲得一個數組中最大的一項(apply會將一個數組裝換爲一個參數接一個參數的傳遞給方法)
這塊在調用的時候第一個參數給了一個null,這個是由於沒有對象去調用這個方法,我只須要用這個方法幫我運算,獲得返回的結果就行,.因此直接傳遞了一個null過去
b)Math.min 能夠實現獲得數組中最小的一項
一樣和 max是一個思想 var min=Math.min.apply(null,array);
c)Array.prototype.push 能夠實現兩個數組合並
一樣push方法沒有提供push一個數組,可是它提供了push(param1,param,…paramN) 因此一樣也能夠經過apply來裝換一下這個數組,即:
vararr1=new Array("1","2","3"); vararr2=new Array("4","5","6"); Array.prototype.push.apply(arr1,arr2);
也能夠這樣理解,arr1調用了push方法,參數是經過apply將數組裝換爲參數列表的集合.
一般在什麼狀況下,可使用apply相似Math.min等之類的特殊用法:
通常在目標函數只須要n個參數列表,而不接收一個數組的形式([param1[,param2[,…[,paramN]]]]),能夠經過apply的方式巧妙地解決這個問題!
一、在JavaScript中,arguments對象是比較特別的一個對象,其實是 當前函數的一個內置屬性。arguments很是相似Array,但實際上又不是一個Array實例。能夠經過以下代碼得以證明(固然,實際上,在函數 funcArg中,調用arguments是沒必要要寫成funcArg.arguments,直接寫arguments便可)。
Array.prototype.testArg = "test"; function funcArg() { alert(funcArg.arguments.testArg); alert(funcArg.arguments[0]); } alert(new Array().testArg); // result: "test" 8 funcArg(10); // result: "undefined" "10"
二、arguments對象的長度是由實參個數而不是形參個數決定的。形參是函數內部從新開闢內存空間存儲的變量,可是其與arguments對象 內存空間並不重疊。對於arguments和值都存在的狀況下,二者值是同步的,可是針對其中一個無值的狀況下,對於此無值的情形值不會得以同步。以下代 碼能夠得以驗證。
function f(a, b, c){ alert(arguments.length); // result: "2" a = 100; alert(arguments[0]); // result: "100" arguments[0] = "qqyumidi"; alert(a); // result: "qqyumidi" alert(c); // result: "undefined" c = 2012; alert(arguments[2]); // result: "undefined" } f(1, 2);
三、由JavaScript中函數的聲明和調用特性,能夠看出JavaScript中函數是不能重載的。
根據其餘語言中重載的依據:"函數返回值不一樣或形參個數不一樣",咱們能夠得出上述結論:
第一:Javascript函數的聲明是沒有返回值類型這一說法的;
第二:JavaScript中形參的個數嚴格意義上來說只是爲了方便在函數中的變量操做,實際上實參已經存儲在arguments對象中了。
另外,從JavaScript函數自己深刻理解爲何JavaScript中函數是不能重載的:在JavaScript中,函數其實也是對象,函數 名是關於函數的引用,或者說函數名自己就是變量。對於以下所示的函數聲明與函數表達式,其實含以上是同樣的(在不考慮函數聲明與函數表達式區別的前提 下),很是有利於咱們理解JavaScript中函數是不能重載的這一特性。
function f(a){ return a + 10; } function f(a){ return a - 10; } // 在不考慮函數聲明與函數表達式區別的前提下,其等價於以下 var f = function(a){ return a + 10; } var f = function(a){ return a - 10; }
四、arguments對象中有一個很是有用的屬性:callee。arguments.callee返回此arguments對象所在的當前函數引用。在使用函數遞歸調用時推薦使用arguments.callee代替函數名自己。
以下:
function count(a){ if(a==1){ return 1; } return a + arguments.callee(--a); } var mm = count(10); alert(mm);
functionName.caller 返回調用者。
看看下面的函數,你們能夠複製到VS中執行下
複製代碼 代碼以下:
function caller() { if (caller.caller) { alert(caller.caller.toString()); } else { alert("函數直接執行"); } } function handleCaller() { caller(); } handleCaller(); caller();
你們會發現第一個alert會彈出調用caller函數的調用者handleCaller,而第二個alert因爲沒有在其餘函數體內調用,因此caller爲null,就執行了 alert("函數直接執行");
返回正被執行的 Function 對象,也就是所指定的 Function 對象的正文.
callee是arguments 的一個屬性成員,它表示對函數對象自己的引用,這有利於匿名
函數的遞歸或者保證函數的封裝性。 下面一段代碼先說明callee的用法,實例代碼摘自網上
複製代碼 代碼以下:
function calleeLengthDemo(arg1, arg2) { alert(arguments.callee.toString()); if (arguments.length == arguments.callee.length) { window.alert("驗證形參和實參長度正確!"); return; } else { alert("實參長度:" + arguments.length); alert("形參長度: " + arguments.callee.length); } } calleeLengthDemo(1);
第一個消息框彈出calleeLengthDemo函數自己,這說明callee就是函數自己對象的引用。callee還有個很是有用的應用就是用來判斷實際參數跟行參是否一致。上面的代碼第一個消息框會彈出實際參數的長度爲1,形式參數也就是函數自己的參數長度爲2.
callee的應用場景通常用於匿名函數
你們看下下面一段代碼 摘自網絡
複製代碼 代碼以下:
var fn=function(n){ if(n>0) return n+fn(n-1); return 0; } alert(fn(10))
函數內部包含了對自身的引用,函數名僅僅是一個變量名,在函數內部調用即至關於調用
一個全局變量,不能很好的體現出是調用自身,這時使用callee會是一個比較好的方法
複製代碼 代碼以下:
var fn=(function(n){ if(n>0) return n+arguments.callee(n-1); return 0; })(10); alert(fn)
這樣就讓代碼更加簡練。又防止了全局變量的污染。
caller的應用場景 主要用於察看函數自己被哪一個函數調用。