標籤:
this、call和apply
this的指向:
this總是指向一個對象,而具體指向哪個對象是在運行時基於函數的執行環境動態綁定的,而非函數被聲明時的環境。
1.作為對象的方法被調用
當函數作為對象的方法被調用時,this指向該對象
2.作為普通函數調用
此時的this總是指向全域對象。在瀏覽器的JavaScript裡,這個全域對象是window對象。
有時候,我們希望div節時間點事件函數內部的this指向該div節點,可以用一個變數儲存div節點的引用:
var that = this;
在ES5的strict 模式下,這種情況下的this已被規定為不會指向全域對象,而是undefined
3.構造器調用
構造器的外表跟普通函數一模一樣,它們的區別在於被調用的方式。當用new運算子調用運算子時,該函數總會返回一個對象,通常情況下,構造器裡的this就指向返回的這個對象。
注意:使用new調用構造器時,還要注意一個問題,如果構造器顯式地返回了一個object類型的對象,那麼此次運算結果最終會返回這個對象,而不是我們之前期待的this。
4.Function.prototype.call或Function.prototype.apply調用
用Function.prototype.call或Function.prototype.apply可以動態改變傳入函數的this。
丟失的this
call和apply
Function.prototype.call或Function.prototype.apply都是非常常用的方法。它們的作用一模一樣,區別僅在於傳入參數形式的不同。
apply接受兩個參數,第一個參數指定了函數體內this對象的指向,第二個參數為一個帶下標的集合,這個集合可以為數組,也可以為類數組,apply方法把這個集合中的元素作為參數傳遞給被調用的函數:
var func = function(a,b,c){ alert([a,b,c]); //輸出[1,2,3]};func.apply(null,[1,2,3]);
call傳入的參數數量不固定,跟apply相同的是,第一個參數也是代表函數體內的this指向,從第二個參數開始往後,每個參數被依次傳入參數:
var func = function(a,b,c){ alert([a,b,c]); //輸出[1,2,3]};func.call(null,1,2,3);
JavaScript的參數在內部就是用一個數組來表示的。從這個意義上來說,apply比call的使用率更高,我們不必關心具體有多少參數被傳入參數,只有用apply一股腦地推過去就可以了。
call是封裝在apply上面的一顆文法糖,明確知道函數接受多少個參數,也可以用call來傳送參數。
當使用call或者apply的時候,如果我們傳入的第一個參數為null,函數體內的this會指向預設的宿主對象,在瀏覽器中則是window,
但如果是在strict 模式下,函數體內的this還是為null。
call和apply的用途:
1.改變this的指向
最常見的用途是改變函數內部的this指向。
2.Function.prototype.bind
Function.prototype.bind,用來指定函數內的this指向
類比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 = function(){ var self = this,//儲存原函數 context = [].shift.call(arguements),//需要綁定的上下文 args = [].slice.call(arguements);//剩餘的參數轉成數組 return function(){ return self.apply(context,[].concat.call(args,[].slice.call(arguements))); //執行新的函數的時候,會把之前傳入的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);
3.借用其他對象的方法
借用方法的第一種情境是"借用建構函式",通過這種技術,可以實現一些類似繼承的效果。
借用方法的第二種運用情境跟我們的關係更加密切。
函數的參數列表arguments是一個類數組對象,雖然它也有"下標",但它並非真正的數組,所以不能像數組一樣,進行排序操作或者往集合裡添加一個新的元素。這種情況下,我們常常會借用Array.prototype對象上的方法。如Array.prototype.push,Array.prototype.slice,Array.prototype.shift等。
JavaScript設計模式與開發實踐---讀書筆記(2) this、call和apply