最開始對于這三者的概念不是很理解,通過查閱資料總算有點理解了,所以在這里記錄一下,有什么不對的地方歡迎指出。
1 定義:call()和apply()其實就是改變函數(shù)的執(zhí)行上下文(this的值),可以劫持另外一個對象的方法,繼承另外一個對象的屬性。他們兩是Function對象的方法,所以每個函數(shù)都能調(diào)用他們。
例如:Function.apply(obj,args)可以說是在obj里面去執(zhí)行Function里面的內(nèi)容。也就是Function的執(zhí)行主體變成了obj,改變函數(shù)的執(zhí)行上下文(this的值)。
2 區(qū)別:call()和apply()的意思是一樣的,不同的是參數(shù)列表不一樣,call的第二部分參數(shù)要一個一個傳,apply要把這些參數(shù)放到數(shù)組中。這就是他們的區(qū)別。
Function.apply(obj,args),F(xiàn)unction.call(obj,[param1[,param2[,…[,paramN]]]])
obj:這個對象將代替Function類里this對象
params:這個是一個參數(shù)列表
apply的一點小擴展:
①實現(xiàn)數(shù)組最大最小的一項??
? ? ? ? ? 因為Math.max 參數(shù)里面不支持Math.max([param1,param2]) 也就是數(shù)組
? ? ? ? ? 所以可以使用Math.max.apply(null,array)或Math.min.apply(null,array)得到數(shù)組最大最小的一項
②可以實現(xiàn)兩個數(shù)組合并
? ? ? ? vararr1=new Array("1","2","3");
? ? ? ? vararr2=new Array("4","5","6");
? ? ? ? Array.prototype.push.apply(arr1,arr2);
③一個數(shù)組插入另一個數(shù)組的指定位置
? ? ? ?var arr1 = ['a', 'b', 'c'];
? ? ? ?var arr2?=?['1',?'2',?'3'];
? ? ? ?arr2.unshift(2,?0);
? ? ? ?Array.prototype.splice.apply(arr1,?arr2);
? ? ? ?console.log(arr1);
3、下面來講bind()函數(shù),bind()是es5中的方法,它也是用來實現(xiàn)上下文綁定,但與bind()和call與apply不同。bind是新創(chuàng)建一個函數(shù),然后把它的上下文綁定到bind()括號中的參數(shù)上,然后將它返回。所以,bind后函數(shù)不會執(zhí)行,而只是返回一個改變了上下文的函數(shù)副本,而call和apply是直接執(zhí)行函數(shù)。
下面代碼可以反映出這點,而且也顯示了bind的用法(后面的代碼皆取自張鑫旭大神的博客)
var button = document.getElementById("button"),? ? text = document.getElementById("text");button.onclick = function() {? ? alert(this.id);// 彈出text}.bind(text);
但由于ie6~ie8不支持該方法,所以若想在這幾個瀏覽器中使用,我們就要模擬該方法,這也是面試??嫉膯栴},模擬的代碼如下:
if (!function() {}.bind) {
Function.prototype.bind = function(context) {
var self = this
, args = Array.prototype.slice.call(arguments);
return function() {
return self.apply(context, args.slice(1));
}
};
}
就是這段代碼,糾正了我長久以來的一個誤區(qū)。下面來講一下這段代碼
首先,我們判斷是否存在bind方法,然后,若不存在,向Function對象的原型中添加自定義的bind方法。
這里面var self = this這段代碼讓我很困擾,按理說,prototype是一個對象,對象的this應(yīng)該指向?qū)ο蟊旧?,也就是prototype,但真的是這樣嗎。看看下面的代碼:
function a(){};
a.prototype.testThis = function(){console.log(a.prototype == this);};
var b = new a();
b.testThis();//false
顯然,this不指向prototype,而經(jīng)過測試,它也不指向a,而指向b。所以原型中的this值就明朗了。指向調(diào)用它的對象。
Array.prototype.slice.call(arguments);
接下來就是上面這段代碼,它會將一個類數(shù)組形式的變量轉(zhuǎn)化為真正的數(shù)組。為啥呢,其實書上并沒有說slice還有這樣的用法,也不知道是誰發(fā)明的。slice的用法可以順便上網(wǎng)查一下,就能查到。但要更正一點,網(wǎng)上的介紹說slice有兩個參數(shù),第一個參數(shù)不能省略。然而我不知道是我理解的問題還是咋地,上面這段代碼tmd不就是典型的沒傳參數(shù)嗎?。。rguments是傳給call的那個上下文,前面講過,不要弄混(由于arguments自己沒有slice方法,這里屬于借用Array原型的slice方法)。而且經(jīng)過測試,若果你不給slice傳參數(shù),那就等于傳了個0給它,結(jié)果就是返回一個和原來數(shù)組一模一樣的副本。
這之后的代碼就很好理解,返回一個函數(shù),該函數(shù)把傳給bind的第一個參數(shù)當做執(zhí)行上下文,由于args已經(jīng)是一個數(shù)組,排除第一項,將之后的部分作為第二部分參數(shù)傳給apply,前面講過apply的用法。
如此,我們自己的這個bind函數(shù)的行為就同es5中的bind一樣了。
總結(jié):call和apply都是改變上下文中的this并立即執(zhí)行這個函數(shù),bind方法可以讓對應(yīng)的函數(shù)想什么時候調(diào)就什么時候調(diào)用,并且可以將參數(shù)在執(zhí)行的時候添加,這是它們的區(qū)別,根據(jù)自己的實際情況來選擇使用。