1. 每個函數都包含兩個非繼承而來的方法:call()方法和apply()方法。
2. 相同點:這兩個方法的作用是一樣的。
都是在特定的作用域中調用函數,等於設置函數體內this對象的值,以擴充函數賴以運行的作用域。
一般來說,this總是指向調用某個方法的對象,但是使用call()和apply()方法時,就會改變this的指向。
call()方法使用示例:
//例1
<script> window.color = 'red'; document.color = 'yellow'; var s1 = {color: 'blue' }; function changeColor(){ console.log(this.color); } changeColor.call(); //red (默認傳遞參數) changeColor.call(window); //red changeColor.call(document); //yellow changeColor.call(this); //red changeColor.call(s1); //blue </script> //例2 var Pet = { words : '...', speak : function (say) { console.log(say + ''+ this.words) } } Pet.speak('Speak'); // 結果:Speak... var Dog = { words:'Wang' } //將this的指向改變成了Dog Pet.speak.call(Dog, 'Speak'); //結果: SpeakWang
apply()方法使用示例:
//例1
<script> window.number = 'one'; document.number = 'two'; var s1 = {number: 'three' }; function changeColor(){ console.log(this.number); } changeColor.apply(); //one (默認傳參) changeColor.apply(window); //one changeColor.apply(document); //two changeColor.apply(this); //one changeColor.apply(s1); //three </script> //例2 function Pet(words){ this.words = words; this.speak = function () { console.log( this.words) } } function Dog(words){ //Pet.call(this, words); //結果: Wang Pet.apply(this, arguments); //結果: Wang } var dog = new Dog('Wang'); dog.speak();
3. 不同點:接收參數的方式不同。
- apply()方法 接收兩個參數,一個是函數運行的作用域(this),另一個是參數數組。
語法:apply([thisObj [,argArray] ]);
,調用一個對象的一個方法,2另一個對象替換當前對象。
說明:如果argArray不是一個有效數組或不是arguments對象,那么將導致一個
TypeError,如果沒有提供argArray和thisObj任何一個參數,那么Global對象將用作thisObj。
- call()方法 第一個參數和apply()方法的一樣,但是傳遞給函數的參數必須列舉出來。
語法:call([thisObject[,arg1 [,arg2 [,...,argn]]]]);
,應用某一對象的一個方法,用另一個對象替換當前對象。
說明: call方法可以用來代替另一個對象調用一個方法,call方法可以將一個函數的對象上下文從初始的上下文改變為thisObj指定的新對象,如果沒有提供thisObj參數,那么Global對象被用於thisObj。
使用示例1:
function add(c,d){ return this.a + this.b + c + d; } var s = {a:1, b:2}; console.log(add.call(s,3,4)); // 1+2+3+4 = 10 console.log(add.apply(s,[5,6])); // 1+2+5+6 = 14
使用示例2:
<script> window.firstName = "Cynthia"; window.lastName = "_xie"; var myObject = {firstName:'my', lastName:'Object'}; function getName(){ console.log(this.firstName + this.lastName); } function getMessage(sex,age){ console.log(this.firstName + this.lastName + " 性別: " + sex + " age: " + age ); } getName.call(window); // Cynthia_xie getName.call(myObject); // myObject getName.apply(window); // Cynthia_xie getName.apply(myObject);// myObject getMessage.call(window,"女",21); //Cynthia_xie 性別: 女 age: 21 getMessage.apply(window,["女",21]); // Cynthia_xie 性別: 女 age: 21 getMessage.call(myObject,"未知",22); //myObject 性別: 未知 age: 22 getMessage.apply(myObject,["未知",22]); // myObject 性別: 未知 age: 22 </script>
4、什么情況下用apply,什么情況下用call
在給對象參數的情況下,如果參數的形式是數組的時候,比如apply示例里面傳遞了參數arguments,這個參數是數組類型,並且在調用Person的時候參數的列表是對應一致的(也就是Person和Student的參數列表前兩位是一致的) 就可以采用 apply , 如果我的Person的參數列表是這樣的(age,name),而Student的參數列表是(name,age,grade),這樣就可以用call來實現了,也就是直接指定參數列表對應值的位置(Person.call(this,age,name,grade));
<script type="text/javascript"> /*定義一個人類*/ function Person(name,age) { this.name=name; this.age=age; } /*定義一個學生類*/ functionStudent(name,age,grade) { Person.apply(this,arguments); //Person.call(this,name,age); 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>
4. apply的一些其他巧妙用法
細心的人可能已經察覺到,在我調用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的方式巧妙地解決這個問題!