
1. 每個(gè)函數(shù)都包含兩個(gè)非繼承而來的方法:call()方法和apply()方法。
2. 相同點(diǎn):這兩個(gè)方法的作用是一樣的。
都是在特定的作用域中調(diào)用函數(shù),等于設(shè)置函數(shù)體內(nèi)this對象的值,以擴(kuò)充函數(shù)賴以運(yùn)行的作用域。
一般來說,this總是指向調(diào)用某個(gè)方法的對象,但是使用call()和apply()方法時(shí),就會改變this的指向。
每個(gè)函數(shù)都包含兩個(gè)非繼承而來的方法:apply()和call()。這兩個(gè)方法的用途都是在特定的作用域中調(diào)用函數(shù),實(shí)際上等于設(shè)置函數(shù)體內(nèi)this對象的值。首先,apply()方法接收兩個(gè)參數(shù):一個(gè)是在其中運(yùn)行函數(shù)的作用域,另一個(gè)是參數(shù)數(shù)組。其中,第二個(gè)參數(shù)可以是Array的實(shí)例,也可以是arguments對象。
function sum(num1, num2){
return num1 + num2;
}
function callSum1(num1, num2){
return sum.apply(this, arguments); // 傳入arguments對象
}
function callSum2(num1, num2){
return sum.apply(this, [num1, num2]); // 傳入數(shù)組
}
console.log(callSum1(10,10)); //20
console.log(callSum2(10,10)); //20
在上面這個(gè)例子中,callSum1()在執(zhí)行sum()函數(shù)時(shí)傳入了this作為this值(因?yàn)槭窃谌肿饔糜蛑姓{(diào)用的,所以傳入的就是window對象)和arguments對象。而callSum2同樣也調(diào)用了sum()函數(shù),但它傳入的則是this和一個(gè)參數(shù)數(shù)組。這兩個(gè)函數(shù)都會正常執(zhí)行并返回正確的結(jié)果。
在嚴(yán)格模式下,未指定環(huán)境對象而調(diào)用函數(shù),則this值不會轉(zhuǎn)型為window。除非明確把函數(shù)添加到某個(gè)對象或者調(diào)用apply()或call(),否則this值將是undefined。
call()方法與apply()方法的作用相同,它們的區(qū)別僅在于接收參數(shù)的方式不同。對于call()方法而言,第一個(gè)參數(shù)是this值沒有變化,變化的是其余參數(shù)都直接傳遞給函數(shù)。換句話說,在使用call()方法時(shí),傳遞給函數(shù)的參數(shù)必須逐個(gè)列舉出來。
function sum(num1, num2){
return num1 + num2;
}
function callSum(num1, num2){
return sum.call(this, num1, num2);
}
alert(callSum(10,10)); //20
關(guān)于this
函數(shù)內(nèi)部的另一個(gè)特殊對象是this,其行為與Java和C#中的this大致類似。換句話說,this引用的是函數(shù)執(zhí)行的環(huán)境對象——或者也可以說是this值(當(dāng)在網(wǎng)頁的全局作用域中調(diào)用函數(shù)時(shí),this對象引用的就是window)。
window.color = "red";
var o = { color: "blue" };
function sayColor(){
alert(this.color);
}
sayColor(); //"red"
o.sayColor = sayColor;
o.sayColor(); //"blue"
上面這個(gè)函數(shù)sayColor()是在全局作用域中定義的,它引用了this對象。由于在調(diào)用函數(shù)之前,this的值并不確定,因此this可能會在代碼執(zhí)行過程中引用不同的對象。當(dāng)在全局作用域中調(diào)用sayColor()時(shí),this引用的是全局對象window;換句話說,對this.color求值會轉(zhuǎn)換成對window.color求值,于是結(jié)果就返回了"red"。而當(dāng)把這個(gè)函數(shù)賦給對象o并調(diào)用o.sayColor()時(shí),this引用的是對象o,因此對this.color求值會轉(zhuǎn)換成對o.color求值,結(jié)果就返回了"blue"。
理解了this,繼續(xù)看call和apply
window.color = "red";
var o = { color: "blue" };
function sayColor(){
alert(this.color);
}
sayColor(); //red
sayColor.call(this); //red
sayColor.call(window); //red
sayColor.call(o); //blue
sayColor.apply(o); //blue
這個(gè)例子是在前面說明this對象的示例基礎(chǔ)上修改而成的。這一次,sayColor()也是作為全局函數(shù)定義的,而且當(dāng)在全局作用域中調(diào)用它時(shí),它確實(shí)會顯示"red"——因?yàn)閷his.color的求值會轉(zhuǎn)換成對window.color的求值。而sayColor.call(this)和sayColor.call(window),則是兩種顯式地在全局作用域中調(diào)用函數(shù)的方式,結(jié)果當(dāng)然都會顯示"red"。但是,當(dāng)運(yùn)行sayColor.call(o)時(shí),函數(shù)的執(zhí)行環(huán)境就不一樣了,因?yàn)榇藭r(shí)函數(shù)體內(nèi)的this對象指向了o,于是結(jié)果顯示的是"blue"。
使用call()(或apply())來擴(kuò)充作用域的最大好處,就是對象不需要與方法有任何耦合關(guān)系。在前面例子的第一個(gè)版本中,我們是先將sayColor()函數(shù)放到了對象o中,然后再通過o來調(diào)用它的;而在這里重寫的例子中,就不需要先前那個(gè)多余的步驟了。
ECMAScript 5還定義了一個(gè)方法:bind()。這個(gè)方法會創(chuàng)建一個(gè)函數(shù)的實(shí)例,其this值會被綁定到傳給bind()函數(shù)的值
window.color = "red";
var o = { color: "blue" };
function sayColor(){
alert(this.color);
}
var objectSayColor = sayColor.bind(o);
objectSayColor(); //blue
在這里,sayColor()調(diào)用bind()并傳入對象o,創(chuàng)建了objectSayColor()函數(shù)。objectSayColor()函數(shù)的this值等于o,因此即使是在全局作用域中調(diào)用這個(gè)函數(shù)。
JavaScript高級程序設(shè)計(jì)(第3版)讀書筆記
GitHub:JavaScript-Demo