apply,call,bind的區(qū)別
1.apply、call
|
1
2
3
4
5
6
7
8
9
10
11
|
function fruits() {}fruits.prototype = { color: "red", say: function() { console.log("My color is " + this.color); }}var apple = new fruits;apple.say(); //My color is red |
但是如果我們有一個(gè)對(duì)象banana= {color : "yellow"} ,我們不想對(duì)它重新定義 say 方法,那么我們可以通過(guò) call 或 apply 用 apple 的 say 方法:
|
1
2
3
4
5
|
banana = { color: "yellow"}apple.say.call(banana); //My color is yellowapple.say.apply(banana); //My color is yellow |
所以,可以看出 call 和 apply 是為了動(dòng)態(tài)改變 this 而出現(xiàn)的,當(dāng)一個(gè) object 沒(méi)有某個(gè)方法(本栗子中banana沒(méi)有say方法),但是其他的有(本栗子中apple有say方法),我們可以借助call或apply用其它對(duì)象的方法來(lái)操作。
2.apply、call 的區(qū)別
對(duì)于 apply、call 二者而言,作用完全一樣,只是接受參數(shù)的方式不太一樣。例如,有一個(gè)函數(shù)定義如下:
|
1
2
3
|
var func = function(arg1, arg2) { }; |
就可以通過(guò)如下方式來(lái)調(diào)用:
|
1
2
|
func.call(this, arg1, arg2); func.apply(this, [arg1, arg2]) |
其中 this 是你想指定的上下文,他可以是任何一個(gè) JavaScript 對(duì)象(JavaScript 中一切皆對(duì)象),call 需要按順序把一組逗號(hào)分隔的數(shù)據(jù)作為參數(shù)傳遞進(jìn)去,而 apply是傳遞一個(gè)數(shù)組,并把數(shù)組第一項(xiàng)作為函數(shù)的第一個(gè)參數(shù),數(shù)組第二項(xiàng)作為函數(shù)第二個(gè)參數(shù),以此類(lèi)推。
|
1
2
3
4
|
var array1 = [12 , "foo" , {name "Joe"} , -2458]; var array2 = ["Doe" , 555 , 100]; Array.prototype.push.apply(array1, array2); /* array1 值為 [12 , "foo" , {name "Joe"} , -2458 , "Doe" , 555 , 100] */ |
2、獲取數(shù)組中的最大值和最小值
|
1
2
3
|
var numbers = [5, 458 , 120 , -215 ]; var maxInNumbers = Math.max.apply(Math, numbers), //458 maxInNumbers = Math.max.call(Math,5, 458 , 120 , -215); //458 |
number 本身沒(méi)有 max 方法,但是 Math 有,方法原型是Math.max(value1[,value2, ...]),我們就可以借助 call 或者 apply 使用其方法。
這里為啥apply會(huì)將第二個(gè)參數(shù)numbers轉(zhuǎn)成單個(gè)的參數(shù)了?它本來(lái)是數(shù)組,這就是apply方法的特性,調(diào)用Math.max方法的時(shí)候,它可以將數(shù)組參數(shù)轉(zhuǎn)換為一個(gè)一個(gè)的參數(shù)。參考MDN上apply方法的原型:
func.apply(thisArg, [argsArray]):apply() 方法調(diào)用一個(gè)具有給定this值的函數(shù),以及作為一個(gè)數(shù)組(或類(lèi)似數(shù)組對(duì)象)提供的參數(shù)。
參數(shù)介紹如下:
參數(shù)
thisArg- 必選的。在
func函數(shù)運(yùn)行時(shí)使用的this值。請(qǐng)注意,this可能不是該方法看到的實(shí)際值:如果這個(gè)函數(shù)處于非嚴(yán)格模式下,則指定為null或undefined時(shí)會(huì)自動(dòng)替換為指向全局對(duì)象,原始值會(huì)被包裝。argsArray- 可選的。一個(gè)數(shù)組或者類(lèi)數(shù)組對(duì)象,其中的數(shù)組元素將作為單獨(dú)的參數(shù)傳給
func函數(shù)。如果該參數(shù)的值為null或undefined,則表示不需要傳入任何參數(shù)。從ECMAScript 5 開(kāi)始可以使用類(lèi)數(shù)組對(duì)象。 瀏覽器兼容性 請(qǐng)參閱本文底部?jī)?nèi)容。
3、驗(yàn)證是否是數(shù)組(前提是toString()方法沒(méi)有被重寫(xiě)過(guò))
|
1
2
3
|
functionisArray(obj){ return Object.prototype.toString.call(obj) === '[object Array]' ;} |
4、類(lèi)(偽)數(shù)組使用數(shù)組方法
|
1
|
var domNodes = Array.prototype.slice.call(document.getElementsByTagName("*")); |
3.深入理解運(yùn)用apply、call
下面就用一個(gè)例子,來(lái)更深入的去理解下 apply 和 call 。
定義一個(gè) log 方法,讓它可以代理 console.log 方法,常見(jiàn)的解決方法是:
|
1
2
3
4
5
|
function log(msg) { console.log(msg);}log(1); //1log(1,2); //1 |
上面方法可以解決最基本的需求,但是當(dāng)傳入?yún)?shù)的個(gè)數(shù)是不確定的時(shí)候,上面的方法就失效了,這個(gè)時(shí)候就可以考慮使用 apply 或者 call,注意這里傳入多少個(gè)參數(shù)是不確定的,所以使用apply是最好的,方法如下:
|
1
2
3
4
5
|
function log(){ console.log.apply(console, arguments);};log(1); //1log(1,2); //1 2 |
接下來(lái)的要求是給每一個(gè) log 消息添加一個(gè)"(app)"的前輟,比如:
|
1
|
log("hello world"); //(app)hello world |
該怎么做比較優(yōu)雅呢?這個(gè)時(shí)候需要想到arguments參數(shù)是個(gè)偽數(shù)組,通過(guò) Array.prototype.slice.call 轉(zhuǎn)化為標(biāo)準(zhǔn)數(shù)組,再使用數(shù)組方法unshift,像這樣:
|
1
2
3
4
5
6
|
function log(){ var args = Array.prototype.slice.call(arguments); args.unshift('(app)'); console.log.apply(console, args);}; |
4.bind
說(shuō)完了 apply 和 call ,再來(lái)說(shuō)說(shuō)bind。bind() 方法與 apply 和 call 很相似,也是可以改變函數(shù)體內(nèi) this 的指向。
MDN的解釋是:bind()方法會(huì)創(chuàng)建一個(gè)新函數(shù),稱(chēng)為綁定函數(shù),當(dāng)調(diào)用這個(gè)綁 定函數(shù)時(shí),綁定函數(shù)會(huì)以創(chuàng)建它時(shí)傳入 bind()方法的第一個(gè)參數(shù)作為 this,傳入 bind() 方法的第二個(gè)以及以后的參數(shù)加上綁定函數(shù)運(yùn)行時(shí) 本身的參數(shù)按照順序作為原函數(shù)的參數(shù)來(lái)調(diào)用原函數(shù)。
直接來(lái)看看具體如何使用,在常見(jiàn)的單體模式中,通常我們會(huì)使用 _this , that , self 等保存 this ,這樣我們可以在改變了上下文之后繼續(xù)引用到它。 像這樣:
|
1
2
3
4
5
6
7
8
9
10
|
var foo = { bar : 1, eventBind: function(){ var _this = this; $('.someClass').on('click',function(event) { /* Act on the event */ console.log(_this.bar); //1 }); }} |
由于 Javascript 特有的機(jī)制,上下文環(huán)境在 eventBind:function(){ } 過(guò)渡到 $('.someClass').on('click',function(event) { }) 發(fā)生了改變,上述使用變量保存 this 這些方式都是有用的,也沒(méi)有什么問(wèn)題。當(dāng)然使用 bind() 可以更加優(yōu)雅的解決這個(gè)問(wèn)題:
|
1
2
3
4
5
6
7
8
9
|
var foo = { bar : 1, eventBind: function(){ $('.someClass').on('click',function(event) { /* Act on the event */ console.log(this.bar); //1 }.bind(this)); }} |
在上述代碼里,bind() 創(chuàng)建了一個(gè)函數(shù),當(dāng)這個(gè)click事件綁定在被調(diào)用的時(shí)候,它的 this 關(guān)鍵詞會(huì)被設(shè)置成被傳入的值(這里指調(diào)用bind()時(shí)傳入的參數(shù))。因此,這里我們傳入想要的上下文 this(其實(shí)就是 foo ),到 bind() 函數(shù)中。然后,當(dāng)回調(diào)函數(shù)被執(zhí)行的時(shí)候, this 便指向 foo 對(duì)象。再來(lái)一個(gè)簡(jiǎn)單的栗子:
|
1
2
3
4
5
6
7
8
9
|
var bar = function(){console.log(this.x);}var foo = {x:3}bar(); // undefinedvar func = bar.bind(foo);func(); // 3 |
這里我們創(chuàng)建了一個(gè)新的函數(shù) func,當(dāng)使用 bind() 創(chuàng)建一個(gè)綁定函數(shù)之后,它被執(zhí)行的時(shí)候,它的 this 會(huì)被設(shè)置成 foo , 而不是像我們調(diào)用 bar() 時(shí)的全局作用域。
有個(gè)有趣的問(wèn)題,如果連續(xù) bind() 兩次,亦或者是連續(xù) bind() 三次那么輸出的值是什么呢?像這樣:
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
var bar = function(){ console.log(this.x);}var foo = { x:3}var sed = { x:4}var func = bar.bind(foo).bind(sed);func(); //? var fiv = { x:5}var func = bar.bind(foo).bind(sed).bind(fiv);func(); //? |
答案是,兩次都仍將輸出 3 ,而非期待中的 4 和 5 。原因是,在Javascript中,多次 bind() 是無(wú)效的。更深層次的原因, bind() 的實(shí)現(xiàn),相當(dāng)于使用函數(shù)在內(nèi)部包了一個(gè) call / apply ,第二次 bind() 相當(dāng)于再包住第一次 bind() ,故第二次以后的 bind 是無(wú)法生效的。
6.apply、call、bind比較
那么 apply、call、bind 三者相比較,之間又有什么異同呢?何時(shí)使用 apply、call,何時(shí)使用 bind 呢。簡(jiǎn)單的一個(gè)栗子:
|
1
2
3
4
5
6
7
8
9
10
11
12
13
|
var obj = { x: 81,};var foo = { getX: function() { return this.x; }}console.log(foo.getX.bind(obj)()); //81console.log(foo.getX.call(obj)); //81console.log(foo.getX.apply(obj)); //81 |
三個(gè)輸出的都是81,但是注意看使用 bind() 方法的,他后面多了對(duì)括號(hào)。
也就是說(shuō),區(qū)別是,當(dāng)你希望改變上下文環(huán)境之后并非立即執(zhí)行,而是回調(diào)執(zhí)行的時(shí)候,使用 bind() 方法。而 apply/call 則會(huì)立即執(zhí)行函數(shù)。
再總結(jié)一下:
- apply 、 call 、bind 三者都是用來(lái)改變函數(shù)的this對(duì)象的指向的;
- apply 、 call 、bind 三者第一個(gè)參數(shù)都是this要指向的對(duì)象,也就是想指定的上下文;
- apply 、 call 、bind 三者都可以利用后續(xù)參數(shù)傳參;
- bind 是返回對(duì)應(yīng)函數(shù),便于稍后調(diào)用;apply 、call 則是立即調(diào)用 。
作者:Tyler Ning
出處:http://www.rzrgm.cn/tylerdonet/
本文版權(quán)歸作者和博客園共有,歡迎轉(zhuǎn)載,但未經(jīng)作者同意必須保留此段聲明,且在文章頁(yè)面明顯位置給出原文連接,如有問(wèn)題,請(qǐng)微信聯(lián)系冬天里的一把火
浙公網(wǎng)安備 33010602011771號(hào)