ECMAScript中的函數是對象,因此函數也有屬性和方法。每個函數都包含兩個屬性:length和prototype,且每個函數包含兩個非繼承而來的方法apply()和call()。這兩個方法的用途都是在特定的作用域中調用函數,實際上等於設置函數體內thiis對象的值。
apply()
apply()方法接受兩個參數:一個是在其中運行函數的作用域,另一個是參數數組。其中,第二個參數可以是Array的實例,也可以是arguments對象。
定義:應用某一對象的一個方法,用另一個對象替換當前對象。即apply()方法能劫持另外一個對象的方法,繼承另外一個對象的屬性.
語法:Function.apply(obj,args)方法能接收兩個參數
說明:obj:這個對象將代替Function類里this對象 args:這個是數組,它將作為參數傳給Function(args-->arguments)
如果 args 不是一個有效的數組或者不是 arguments 對象,那么將導致一個 TypeError。
如果沒有提供 args 和 obj 任何一個參數,那么 Global 對象將被用作 obj, 並且無法被傳遞任何參數。
call()
apply()和call()方法的作用相同,它們的區別僅在於接收參數的方式不同。對於call()方法而言,第一個參數是this值沒有變化,變卦的是其余參數都直接傳遞給函數。換句話說,在使用call()方法時,傳遞給函數的參數必須逐個列舉出來。
語法:Function.call(obj,[param1[,param2[,…[,paramN]]]])
定義:調用一個對象的一個方法,以另一個對象替換當前對象。
說明: obj:這個對象將代替Function類里this對象 params:這個是一個參數列表
call 方法可以用來代替另一個對象調用一個方法。call 方法可將一個函數的對象上下文從初始的上下文改變為由 thisObj 指定的新對象。
如果沒有提供 obj 參數,那么 Global 對象被用作 obj。
舉例:
1 <script type="text/javascript"> 2 /*定義一個人類*/ 3 function Person(name,age) { 4 this.name=name; this.age=age; 5 } 6 /*定義一個學生類*/ 7 function Student(name,age,grade) { 8 Person.apply(this,arguments); this.grade=grade; 9 } 10 //創建一個學生類 11 var student=new Student("qian",21,"一年級"); 12 //測試 13 alert("name:"+student.name+"\n"+"age:"+student.age+"\n"+"grade:"+student.grade); 14 //大家可以看到測試結果name:qian age:21 grade:一年級 15 //學生類里面我沒有給name和age屬性賦值啊,為什么又存在這兩個屬性的值呢,這個就是apply的神奇之處. 16 </script>
分析: Person.apply(this,arguments);
this:在創建對象在這個時候代表的是student
arguments:是一個數組,也就是[“qian”,”21”,”一年級”];
也就是通俗一點講就是:用student去執行Person這個類里面的內容,在Person這個類里面存在this.name等之類的語句,這樣就將屬性創建到了student對象里面
主要解決的幾個問題
apply和call的區別在哪里(僅僅只是接受參數的方式不同)
什么情況下用apply,什么情況下用call
在給對象參數的情況下,如果參數的形式是數組的時候,比如apply示例里面傳遞了參數arguments,這個參數是數組類型,並且在調用Person的時候參數的列表是對應一致的(也就是Person和Student的參數列表前兩位是一致的) 就可以采用 apply ;如果我的Person的參數列表是這樣的(age,name),而Student的參數列表是(name,age,grade),這樣就可以用call來實現了,也就是直接指定參數列表對應值的位置(Person.call(this,age,name,grade))。 在不給函數傳遞參數的情況下,使用哪個方法都無所謂。
apply的其他巧妙用法(一般在什么情況下可以使用apply)
調用apply方法的時候,第一個參數是對象(this), 第二個參數是一個數組集合, 這里就說明apply的一個巧妙用法,可以將一個數組默認的轉換為一個參數列表([param1,param2,param3] 轉換為 param1,param2,param3), 這個如果讓我們用程序來實現將數組的每一個項,來裝換為參數的列表,可能都得費一會功夫,借助apply的這點特性,所以就有了以下高效率的方法。
(1)Math.max 實現得到數組中最大的一項
因為Math.max 參數里面不支持Math.max([param1,param2]) 也就是數組 ,但是它支持Math.max(param1,param2,param3…),所以
可以根據剛才apply的那個特點來解決 var max=Math.max.apply(null,array),這樣輕易的可以得到一個數組中最大的一項 (apply會將一個數組裝換為一個參數接一個參數的傳遞給方法)
這塊在調用的時候第一個參數給了一個null,這個是因為沒有對象去調用這個方法,我只需要用這個方法幫我運算,得到返回的結果就行,.所以直接傳遞了一個null。
var arr = new Array(1,2,3,4,5); var max = Math.max.apply(null,arr); //5
(2)Array.prototype.push 可以實現兩個數組合並
同樣push方法沒有提供push一個數組,但是它提供了push(param1,param,…paramN);
var arr1=new Array("1","2","3"); var arr2=new Array("4","5","6");
如果我們要把 arr2展開,然后一個一個追加到arr1中去,最后讓arr1=[“1”,“2”,“3”,“4”,“5”,“6”]
arr1.push(arr2)顯然是不行的,因為這樣做會得到[“1”,“2”,“3”,[“4”,“5”,“6”]]
我們只能用一個循環去一個一個的push(當然也可以用arr1.concat(arr2),但是concat方法並不改變arr1本身)
var arrLen=arr2.length; for(var i=0;i<arrLen;i++){ arr1.push(arr2[i]); }
所以同樣也可以通過apply來轉換一下這個數組,即:
var arr1=new Array("1","2","3"); var arr2=new Array("4","5","6"); Array.prototype.push.apply(arr1,arr2);
也可以這樣理解,arr1調用了push方法,參數是通過apply將數組裝換為參數列表的集合.。
bind()方法
ECMAScript5還定義了一個方法:bind()。這個方法會創建一個函數的實例,其this值會被綁定到傳給bind()函數的值。
1 <script type="text/javascript"> 2 3 window.color = "red"; 4 var o = { color: "blue" }; 5 6 function sayColor(){ 7 alert(this.color); 8 } 9 var objectSayColor = sayColor.bind(o); 10 objectSayColor(); //blue 11 12 13 </script>
在這里,sayColor()調用bing()並傳入對象o,創建了objectSayColor()函數。objectSayColor()函數的this值等於o,因此即使是在全局作用域中調用這個函數,也會看到“blue”。
bind()主要是為了改變函數內部的this指向,這個是在ECMA5以后加入的,所以IE8一下的瀏覽器不支持。
bind()方法會創建一個新函數,稱為綁定函數.當調用這個綁定函數時,綁定函數會以創建它時傳入bind方法的第一個參數作為this,傳入bind方法的第二個以及以后的參數加上綁定函數運行時本身的參數按照順序作為原函數的參數來調用原函數.
參考:Js apply()使用詳解 js高程P116-118
