在web項目中Javascript是一門必須要掌握的動態語言,基於Javascript的框架大多離不開不了最基礎的Javascript的用法和原理。本文主要是總結一下Javascript中那萬惡的this關鍵字。
開門見山,拋出一個觀點:“Javascript中this永遠是指向調用它的對象”。下面我會舉3個最有代表性的例子來驗證我的這個觀點。
例一 對象方法調用
var x = 1; function testThis(){ console.log(this.x); } testThis(); // 這里聲明了一個全局變量x,一個全局方法,這兩個對象都綁定在Window上,所以當運行testThis()的結果就是取Window對象上的x成員,結果是1 var o={}; o.x = 5; o.method = testThis; o.method(); // 此時,我們講o對象的method指向了testThis, 當調用它的時候,this指向調用他的對象,這是x就是去o對象的x,結果是5
這個例子十分基礎,也就是常見的對象調用方法的時候,方法里面的this就是指向調用他或者是擁有他的對象
例二 構造函數創建
var x = 2; function test(){ this.x = 1; } var o = new test(); console.log(o.x); // 1 //這就是javascript中的構造函數,通過new來創建一個實例,這里取的值是綁定在對象o里面的x,所以是1 //下面這個例子是Angular中的Service,你可以直接理解成他會通過第二個參數new一個單例對象 app.service("MyService", function ($http, $modal) { this.test = function() { console.log(this); test2(); } function test2() { console.log(this) } } MyService.test() // 打印 MyService {test: function} 和 Window {}
//這里log出來雖然test2()可以被test1()調用,但它其實並不屬於Service, 所以如果test2里面調用this,就會出現常見的錯誤,哎呀媽呀,咋調不了自己的方法?!
這個例子我們項目中經常出現,而且很難解釋清楚,就像還有人問我為什么controller中不直接使用this,而是要用$scope來綁定方法和變量,當然用this能夠取代部分$scope,但是難免遇到this的上下文不同引起的一系列問題。這個問題的關鍵就是test2方法並不屬於對象Service,但是由於在service閉包(closure)里面,他可以被Service調用,所以test2里面的this就不是指向Service,從而調用Service里面的其他方法就會報錯。
例三 改變this指向
function test(){ console.log(this.x); } var o={}; o.x = 1; o.m = test; o.m.apply({x:5}); //5 o.m.call() // undefined //通過apply/call來指定調用函數的this作用上下文,都是指用參數對象來調用o對象的函數,默認參數是Global
其實通過這個例子,大家就已經可以看到this的指向是不確定的,this值在進入上下文時確定,並且在上下文運行期間永久不變。上面的例子改變this的上下文,導致兩次結果不一致也是最好的證據。
最后
本文最大的作用就是如果看完本文你能夠理解this為什么有時候會跟自己期望不一樣,而且能得到一個避免這樣問題發生的解決方案,那我的目的就達到了。很幸運你能看到這里,解決方案呈現給大家:就像Angular要將屬性和方法給你提供一個$scope來綁定屬性,也像coffeeScript會在對象一開頭有這么個賦值“ _this = this”,我們在自己的js實踐中不妨定義"val self = this",在之后的作用域里面用self來操作對象的屬性,這是解決之道。