關於this的指向問題算的上是js中的一個十分重要的問題了。今天把這個問題總結下,加深下本身對this的理解。
首先,this的指向問題能夠用一句話總結就是:this老是指向調用的對象,也就是說this指向誰與函數聲明的位置沒有關係,只與調用的位置有關。這是判斷this的一個大致原則,而具體的小原則按照優先級的不一樣大體能夠分爲如下幾點:app
new方式是優先級最高的一種調用方式,也就是說只要是出現new方式來調用一個函數,this確定會指向new調用函數新建立的對象。函數
function() thisTo(a){ this.a=a; } var data=new thisTo(2); //在這裏進行了new綁定 console.log(data.a); //2
顯示綁定指的是經過call()和apply()方法對函數進行的調用,對this影響的優先級僅次於new綁定。this
function thisTo(){ console.log(this.a); } var data={ a:2 }; thisTo.call(data)); //2
隱式綁定是指經過對象的屬性進行添加,從而調用this所在函數,該方式的優先級在顯示綁定以後。編碼
function thisTo(){ console.log(this.a); } var data={ a:2, foo:thisTo //經過屬性引用this所在函數 }; data.foo(); //2
默認綁定是指當上面這三條綁定規則都不符合時採用的綁定規則,默認綁定會把this默認綁定到全局對象中,是優先級最低的綁定規則。prototype
function thisTo(){ console.log(this.a); } var a=2; //a是全局對象的一個同名屬性 thisTo(); //2
當進行隱式綁定時,若是進行一次引用賦值或者傳參操做,會形成this的丟失,從而最後將this綁定到全局對象中去。code
1.1 引用賦值丟失對象
function thisTo(){ console.log(this.a); } var data={ a:2, foo:thisTo //經過屬性引用this所在函數 }; var a=3;//全局屬性 var newData=data.foo; //這裏進行了一次引用賦值 newData(); // 3
原理:由於newData實際上引用的是foo函數自己,跟data對象沒有任何關係,data對象只是一箇中間橋樑。而newData就是一個自己不帶a屬性的對象,天然最後只能把a綁定到全局對象上了。ip
1.2傳參丟失作用域
function thisTo(){ console.log(this.a); } var data={ a:2, foo:thisTo //經過屬性引用this所在函數 }; var a=3;//全局屬性 setTimeout(data.foo,100);// 3
原理:setTimeout(fn,delay) { fn(); } 實際上fn是一個參數傳遞的引用(fn=data.foo),與引用丟失的原理同樣io
1.3 Function.prototype.bind()
爲了解決隱式丟失的問題,ES5提供了bind方法,bind()會返回一個硬編碼的新函數,它會把參數設置爲this的上下文並調用原始函數。
function thisTo(){ console.log(this.a); } var data={ a:2 }; var a=3; var bar=thisTo.bind(data); console.log(bar()); //2
間接引用是指一個定義對象的方法引用另外一個對象存在的方法,這種狀況下會使得this進行默認綁定。
function thisTo(){ console.log(this.a); } var data={ a:2, foo:thisTo }; var newData={ a:3 } var a=4; data.foo(); //2 (newData.foo=data.foo)() //4
原理:newData.foo=data.foo的返回值是目標函數的引用,所以調用的位置其實是foo(),根據以前的隱式丟失裏面說的原則,這裏會應用默認綁定。
ES6的箭頭函數在this這塊是一個特殊的改進,箭頭函數使用了詞法做用域取代了傳統的this機制,因此箭頭函數沒法使用上面所說的這些this優先級的原則,注意的是在箭頭函數中,是根據外層父親做用域來決定this的指向問題。
function thisTo(){ setTimeout(function(){ console.log(this.a); },100); } var obj={ a:2 } var a=3; thisTo.call(obj); //3
不用箭頭函數,發生隱式丟失,最後的this默認綁定到全局做用域,輸出3。
function thisTo(){ setTimeout(()=>{ console.log(this.a); },100); } var obj={ a:2 } var a=3;加粗文字 thisTo.call(obj); //2
用了箭頭函數,不會發生隱式丟失,this綁定到外層父做用域thisTO(),thisTo的被調用者是obj對象,因此最後的this到obj對象中,輸出2。
若是不用箭頭函數實現相同的輸出,能夠採用下面這種方式:
function thisTo(){ var self=this; //在當前做用域中捕獲this setTimeout(function(){ console.log(self.a); //傳入self代替以前的this },100); } var obj={ a:2 } var a=3; thisTo.call(obj); //2
this的綁定機制,就是要找到這個函數的直接調用位置,而後應用綁定的四條規則,當出現知足多個規則時,按照優先級的高低決定最終的綁定規則。此外注意幾種特殊狀況,特別是ES6中的箭頭函數。
你不知道的JavaScript上卷