apply 接受兩個參數,第一個參數指定了函數體內this 物件的指向,第二個參數為一個帶下標的集合,這個集合可以為數組,也可以為類別數組,apply 方法把這個集合中的元素作為參數傳遞給被呼叫的函數:
var func = function( a, b, c ){ alert ( [ a, b, c ] ); // 输出 [ 1, 2, 3 ] }; func.apply( null, [ 1, 2, 3 ] );
在這段程式碼中,參數1、2、3 被放在數組中一起傳入func函數,它們分別對應func參數列表中的a、b、 c。
call 傳入的參數數量不固定,跟apply 相同的是,第一個參數也是代表函數體內的this 指向,從第二個參數開始往後,每個參數被依次傳入函數:
var func = function( a, b, c ){ alert ( [ a, b, c ] ); // 输出 [ 1, 2, 3 ] }; func.call( null, 1, 2, 3 );
當呼叫一個函數時,JavaScript 的解釋器並不會會計較形參在數量、類型以及順序上的區別,JavaScript 的參數在內部就是用一個陣列來表示的。從這個意義上說,apply比call的使用率更高,我們不必關心具體有多少參數被傳入函數,只要用apply 一股腦地推過去就可以了。 call是包裝在apply上面的一顆語法糖,如果我們明確地知道函數接受多少個參數,而且想一目了然地表達形參和實參的對應關係,那麼也可以用call 來傳送參數。
call和apply的用途
1. 改變this 指向
call 和apply 最常見的用途是改變函數內部的this 指向,我們來看個例子:
var obj1 = { name: 'sven' }; var obj2 = { name: 'anne' }; window.name = 'window'; var getName = function(){ alert ( this.name ); }; getName(); // 输出: window getName.call( obj1 ); // 输出: sven getName.call( obj2 ); // 输出: anne
當執行getName.call(這句程式碼時,getName 函數體內的this 就指向obj1 對象,所以此處的
var getName = function(){ alert ( this.name ); };
實際上相當於:
var getName = function(){ alert ( obj1.name ); // 输出: sven };
在實際開發中,經常會遇到this指向被不經意改變的場景,例如有一個div節點,div節點的onclick 事件中的this 本來是指向這個div的:
document.getElementById( 'div1' ).onclick = function(){ alert( this.id ); // 输出:div1 };
假如該事件函數中有一個內部函數func,在事件內部調用func 函數時,func 函數體內的this就指向了window,而不是我們預期的div,見如下程式碼:
document.getElementById( 'div1' ).onclick = function(){ alert( this.id ); // 输出:div1 var func = function(){ alert ( this.id ); // 输出:undefined } func(); };
這時候我們用call 來修正func 函數內的this,使其依然指向div:
document.getElementById( 'div1' ).onclick = function(){ var func = function(){ alert ( this.id ); // 输出:div1 } func.call( this ); };
2. Function. prototype.bind
大部分高階瀏覽器都實作了內建的Function.prototype.bind,用來指定函數內部的this 指向,即使沒有原生的Function.prototype.bind 實現,我們來模擬一個也不是難事,程式碼如下:
Function.prototype.bind = function( context ){ var self = this; // 保存原函数 return function(){ // 返回一个新的函数 return self.apply( context, arguments ); // 执行新的函数的时候,会 把之前传入的context // 当作新函数体内的this } }; var obj = { name: 'sven' }; var func = function(){ alert ( this.name ); // 输出:sven }.bind( obj); func();
我們透過Function.prototype.bind 來「包裝」func 函數,並且傳入一個物件context 當作參數,這個context 物件就是我們想要修正的this 物件。
在Function.prototype.bind 的內部實作中,我們先把func 函數的引用保存起來,然後再回傳一個新的函數。當我們將來執行func 函數時,實際上先執行的是這個剛剛回傳的新函數。在新函數內部,self.apply( context, arguments )這句程式碼才是執行原來的func 函數,並且指定context物件為func 函數體內的this。
這是一個簡化版的Function.prototype.bind 實現,通常我們還會把它實現得稍微複雜一點,
使得可以往func 函數中預先填入一些參數:
Function.prototype.bind = function(){ var self = this, // 保存原函数 context = [].shift.call( arguments ), // 需要绑定的this 上下文 args = [].slice.call( arguments ); // 剩余的参数转成数组 return function(){ // 返回一个新的函数 return self.apply( context, [].concat.call( args, [].slice.call( arguments ) ) ); // 执行新的函数的时候,会把之前传入的context 当作新函数体内的this // 并且组合两次分别传入的参数,作为新函数的参数 } }; var obj = { name: 'sven' }; var func = function( a, b, c, d ){ alert ( this.name ); // 输出:sven alert ( [ a, b, c, d ] ) // 输出:[ 1, 2, 3, 4 ] }.bind( obj, 1, 2 ); func( 3, 4 );
以上就是本文的全部內容,希望本文的內容對大家的學習或工作能帶來一定的幫助,同時也希望多多支持PHP中文網!
更多JavaScript中的call和apply的用途以及區別相關文章請關注PHP中文網!