開始看到javascript的函數apply和call時,非常的模糊,由于最近在找工作應對面試,在網上看到一些文章對apply方法和call的一些示例,我就想把我的理解的知識點做個筆記;
1、首先我先拋出下面的問題,跟著問題去看會加深記憶:
1.什么情況下用apply,什么情況下用call ,2.apply的其他巧妙用法(一般在什么情況下可以使用apply)3.apply和call的區別在哪里
首先介紹一下apply() 是做什么的
1、apply([thisObj[,argArray]]) :應用某一對象的一個方法,用另一個對象替換當前對象。如果 argArray 不是一個有效的數組或者不是 arguments 對象,那么將導致一個 TypeError。如果沒有提供 argArray 和 thisObj 任何一個參數,那么 Global 對象將被用作 thisObj, 并且無法被傳遞任何參數。
2、call:調用一個對象的一個方法,以另一個對象替換當前對象。call 方法可以用來代替另一個對象調用一個方法。call 方法可將一個函數的對象上下文從初始的上下文改變為由 thisObj 指定的新對象。如果沒有提供 thisObj 參數,那么 Global 對象被用作 thisObj。
apply() , call() 實例:
function Person(name,age) {
? ? ? this.name=name;
? ? ? this.age=age;
}
apply :/*定義一個學生類*/
function Student1(name,age,grade)?{
? ? ? Person.apply(this,arguments);
? ? ?this.grade=grade;
}
call:/*定義一個學生類*/
function Student2(name,age,grade)?{
? ? Person.call(this,name,age);
? ? ?this.grade=grade;
}
//創建一個學生類
var student1 = new Student1("qian",21,"一年級");
var student2 = new Student2("qian",21,"一年級");
//測試
alert("name:"+student1.name+"\n"+"age:"+student1.age+"\n"+"grade:"+student1.grade);
alert("name:"+student2.name+"\n"+"age:"+student2.age+"\n"+"grade:"+student2.grade);
//大家可以看到測試結果都為: name : qian?age : 21?grade : 一年級
Student1 //學生類里面我沒有給name和age屬性賦值啊,為什么又存在這兩個屬性的值呢,這個就是apply的妙用之處.
分析:Student1中 Person.apply(this,arguments); 和 Student2 中 Person.call(this,name,age);
this:在創建對象在這個時候代表的是student?
arguments:是一個數組,也就是[“qian”,”21”,”一年級”];
也就是:用student1去執行Person這個類里面的內容,在Person這個類里面存在this.name等之類的語句,這樣就將屬性創建到了student1對象里面? 而 student2 中我就不過多解釋了
2、什么情況下用apply,什么情況下用call
在給對象參數的情況下,如果參數的形式是數組的時候,比如apply示例里面傳遞了參數arguments,這個參數是數組類型,并且在調用Person的時候參數的列表是對應一致的(也就是Person和Student1的參數列表前兩位是一致的) 就可以采用 apply , 如果我的Person的參數列表是這樣的(age,name),而Student1的參數列表是(name,age,grade),這樣就可以用call來實現了,也就是直接指定參數列表對應值的位置(Person.call(this,age,name,grade));
3、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的那個特點來解決varmax=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來裝換一下這個數組,即:
var arr1 = newArray("1","2","3");
var arr2 = newArray("4","5","6");
Array.prototype.push.apply(arr1,arr2);
也可以這樣理解,arr1調用了push方法,參數是通過apply將數組裝換為參數列表的集合.
通常在什么情況下,可以使用apply類似Math.min等之類的特殊用法:
一般在目標函數只需要n個參數列表,而不接收一個數組的形式([param1[,param2[,…[,paramN]]]]),可以通過apply的方式巧妙地解決這個問題!
常用實例實例中??
apply 和call 是可以實現繼承和多重繼承
繼承
function Animal(name,age){
? ? ? this.name?=?name;
? ? ? this.age = age;
? ? ? ? this.showName?=?function(){
? ? ? ? ? ? ?alert(this.name);
? ? ? ? }
? ? ? ? this.showAge = function(){
? ? ? ? ? ? ?alert(this.age);
? ? ? ? }
}
function?Cat1(name,age){
? ? ? ? ? ? Animal.call(this,?name,age);
}
function Cat2(name,age){
? ? ?Animal.apply(this,arguments);
}
var?cat2 = new Cat1("Black?Cat",''1');
var cat2 = new Cat2("Black Cat");
cat1.showName();
cat2.showName();
Animal.call(this) 與Animal.apply(this) 的意思就是使用 Animal對象代替this對象,那么 Cat1,Cat2中不就有Animal的所有屬性和方法了嗎,Cat1,Cat2 對象就能夠直接調用Animal的方法以及屬性了.
多重繼承
function Class10(){
? ? ? ?this.showSub?=?function(a,b){
? ? ? ? ? ?alert(a-b);
? ? ? }
}
function?Class11(){
? ? ? ?this.showAdd?=?function(a,b) {
? ? ? ? ? ? ?alert(a+b);
? ? ? ?}
}
function?Class2() {
? ? ? Class10.call(this);
? ? ?Class11.call(this);
}
function Class3() {
? ? ? ?Class10.apply(this);
? ? ? ?Class11.apply(this);
}
很簡單,使用兩個 call或apply? 就實現多重繼承了
當然,js的繼承還有其他方法,例如使用原型鏈,apply 和 call 這兩個方法基本上是一個意思,區別在于 call 的第二個參數可以是任意類型,而apply的第二個參數必須是數組,也可以是arguments