apply()和call()是ES3引入的新方法。為啥要引入這兩個方法呢泰涂?為了更優(yōu)雅的實現(xiàn)面向?qū)ο缶幊痰睦^承鲫竞。另外apply還有一個妙用。
區(qū)分apply,call就一句話:
foo.call(this,arg1,arg2,arg3)==foo.apply(this,arguments)==this.foo(arg1, arg2, arg3)
所以逼蒙,它們的作用一樣从绘,只是方法傳遞的參數(shù)不同。
call(..)和apply(..)的語法中的相同點是是牢,第一個參數(shù)都是一個對象僵井,語法中的區(qū)別是,后面?zhèn)魅氲膮?shù)的形式不同妖泄,call()方法接受的是若干個參數(shù)的列表驹沿,而apply()方法接受的是一個包含多個參數(shù)的數(shù)組。下面分別介紹:
call方法:
語法 fun.call(thisArg[, arg1[, arg2[, ...]]])
thisArg是在fun函數(shù)運行時指定的this值蹈胡。需要注意的是下面幾種情況:
(1)不傳,或者傳null朋蔫,undefined罚渐,函數(shù)中的this指向window對象
(2)傳遞另一個函數(shù)的函數(shù)名,函數(shù)中的this指向這個函數(shù)的引用驯妄,并不一定是該函數(shù)執(zhí)行時真正的this值
(3)值為原始值(數(shù)字荷并,字符串,布爾值)的this會指向該原始值的自動包裝對象青扔,如 String源织、Number、Boolean
(4)傳遞一個對象微猖,函數(shù)中的this指向這個對象
arg1, arg2, ...是指定的參數(shù)列表谈息,多余的參數(shù)將被自動忽略。
//定義函數(shù)a
function a(){
//輸出函數(shù)a中的this對象
console.log(this);
}
//定義函數(shù)b
function b(){}
//定義對象obj
var obj = {name:'這是一個屌絲'};
a.call(); //window
a.call(null); //window
a.call(undefined);//window
a.call(1); //Number
a.call(''); //String
a.call(true); //Boolean
a.call(b);// function b(){}
a.call(obj); //Object
所以凛剥,foo.call(bar, arg1, arg2, arg3...)的執(zhí)行過程如下侠仇,foo.apply(bar, [arg1, arg2, arg3...])也一樣道理:
第一步,改變foo函數(shù)的this指向;
第二步逻炊,給foo函數(shù)傳參互亮;
第三步,執(zhí)行foo函數(shù)余素。
例豹休,最簡用法:
function foo() {
console.log( this.a );
}
var obj = {
a: 2
};
foo.call(obj); // 2,將call換成apply也成立
foo.call(obj);
怎么理解桨吊?像上面說的慕爬,第一步,foo的this指向了obj屏积,第二步傳參略過医窿,第三步,執(zhí)行foo炊林,foo中的console.log( this.a );
的this由于指向obj姥卢,所以是打印2≡郏可以再看一個例子:
function greet() {
var reply = this.person + '是一個' + this.role + '独榴。';
console.log(reply);
}
var i = {
person: '我',
role: '演員'
};
greet.call(i);
// 我是一個演員。
例奕枝,帶參數(shù)用法:
function foo(a, b) {
return a + '-' + b + '-' + this.c;
}
var bar = {};
bar.c = 100;
console.log( foo.call(bar, 1, 10) ); // 1-10-100
console.log( foo.apply(bar, [2, 20]) ); // 2-20-100
第一步棺榔,foo的this指向bar的引用,第二步隘道,給foo傳參1和10症歇,第三步,執(zhí)行foo谭梗,得到1-10-100忘晤。
apply方法:
語法與 call() 方法的語法幾乎完全相同,唯一的區(qū)別在于激捏,apply的第二個參數(shù)必須是一個包含多個參數(shù)的數(shù)組(或類數(shù)組對象)设塔。
語法:fun.apply(thisArg[, argsArray])
thisArg同上call的thisArg參數(shù)。
argsArray是一個數(shù)組或者類數(shù)組對象远舅,其中的數(shù)組元素將作為單獨的參數(shù)傳給 fun 函數(shù)闰蛔。如果該參數(shù)的值為null 或 undefined,則表示不需要傳入任何參數(shù)图柏。從ECMAScript 5開始可以使用類數(shù)組對象序六。需要注意:Internet Explorer 8和9不接受類數(shù)組對象。如果傳入類數(shù)組對象爆办,它們會拋出異常难咕。
例,使用apply方法調(diào)用父構(gòu)造函數(shù),實現(xiàn)繼承:
// 定義一個人構(gòu)造函數(shù)余佃,是父構(gòu)造函數(shù)
function Person(name,age)
{
this.name = name;
this.age = age;
}
// 定義一個學(xué)生構(gòu)造函數(shù)暮刃,是子構(gòu)造函數(shù)
function Student(name,age,grade)
{
Person.apply(this, arguments);
this.grade = grade;
}
// 創(chuàng)建一個學(xué)生實例
var student=new Student("Jack",7,"one");
// 測試
alert("name:" + student.name + "\n" + "age:" + student.age + "\n" + "grade:" + student.grade);
// 學(xué)生類里面并沒有給name和age屬性賦值,為什么又存在這兩個屬性的值呢爆土,這個就是apply的神奇之處
new出student的過程椭懊,我們看看發(fā)生了什么:
new操作符的操作,先復(fù)習(xí)一下:
1步势、創(chuàng)建一個隱藏連接到該函數(shù)的prototype成員的新對象氧猬。
2、將函數(shù)的作用域賦給新對象坏瘩,所以this也被綁定到那個新對象上盅抚。
3、像執(zhí)行普通函數(shù)一樣倔矾,執(zhí)行一遍函數(shù)妄均。
4、返回這個新對象哪自。
于是丰包,第一步,student對象誕生壤巷,第二步邑彪,Student的作用域賦給了student,Student的this也指向了student的引用胧华,第三步寄症,執(zhí)行函數(shù),執(zhí)行函數(shù)又分為下面步驟:
第一句撑柔,Person.apply(this,arguments);
瘸爽,它的作用是:
第一步,讓Person的this綁定到Student的引用上铅忿。
第二步,將name,age,grade傳參給Person構(gòu)造函數(shù)灵汪。
第三步檀训,執(zhí)行Person函數(shù)一遍,也就是給Student new出的實例對象添加了name享言、age兩個屬性峻凫。
然后還有一句,this.grade = grade;
览露,它是給new出的實例對象添加了一個grade屬性荧琼。
現(xiàn)在的student實例對象就有了三個屬性,當(dāng)然它就可以打印出三個屬性。
為內(nèi)置函數(shù)使用apply方法
聰明的apply用法允許你在某些本來需要寫成遍歷數(shù)組變量的任務(wù)中使用內(nèi)建的函數(shù)命锄。在接下里的例子中我們會使用Math.max/Math.min來找出一個數(shù)組中的最大/最小值堰乔。
我們先復(fù)習(xí)一下Math.max()的常規(guī)用法:
console.log(Math.max(7.25, 7.3, 22, 1, 35)); // 35
雖然很簡單就得到了最大值,但是脐恩,Math.max()只接受參數(shù)列表镐侯,不接受數(shù)組參數(shù)。怎么辦驶冒?你不接受苟翻,自有人接受,apply接受數(shù)組參數(shù)骗污。用apply傳遞數(shù)組參數(shù)崇猫,如下:
var numbers = [5, 6, 2, 3, 7, 11];
var max = Math.max.apply(null, numbers);
var min = Math.min.apply(null, numbers);
console.log(max);
console.log(min);
// 如果不用apply,只能是手寫需忿,參數(shù)很少的話還好诅炉,多的話會死人的:
var max = Math.max(numbers[0], numbers[1], numbers[2], numbers[3], numbers[4], numbers[5]);
console.log(max);
所以,apply的一個特殊用途就是給只接受參數(shù)列表的函數(shù)傳遞數(shù)組參數(shù)贴谎。
總結(jié)
目前面向?qū)ο缶幊痰睦^承方式汞扎,主要是call/apply方式,和原型鏈方式擅这。IE6開始支持他們所有方式澈魄,放心使用即可。
call和apply唯一區(qū)別在于call接受參數(shù)列表仲翎,apply接受數(shù)組參數(shù)或類數(shù)組參數(shù)痹扇。利用apply的這個特性,可以給任意不接受數(shù)組參數(shù)的原生函數(shù)改寫成支持接受數(shù)組參數(shù)溯香。其他時候根據(jù)實際情況選擇用call還是apply鲫构。