理解 this 作用域
理解this作用域
轉載https://github.com/AwesomeDevin/blog/issues/10
《javascript高階程式設計》中有說到:
this物件是在執行時基於函式的執行環境繫結的:在全域性函式中,this等於window ,而當函式被作為某個物件呼叫時,this等於那個物件。不過,
匿名函式具有全域性性,因此this物件同常指向window
不過,在全域性函式中,this等於window
,匿名函式具有全域性性,因此this物件通常指向window
,針對於匿名函式this具有全域性性的觀點仍是有爭議的,可參考 https://www.zhihu.com/question/21958425
this的指向取決於函式(不包含箭頭函式)執行時的環境
驗證過程如下:
關於閉包經常會看到這麼一道題:
var name = "The Window"; var object = { name : "My Object", getNameFunc : function(){ return function(){ return this.name; }; } }; console.log(object.getNameFunc()());//result:The Window
在這裡,getNameFunc
return了1個匿名函式,可能你會認為這就是輸出值為The Window
的原因
但是,我們再來嘗試寫1個匿名函式
var name = "The Window"; var object = { name : "My Object", getNameFunc : function(){ return this.funAA; }, funAA:function(){ return this.name } }; console.log(object.getNameFunc()(),object.funAA())
可以發現,同樣是匿名函式,卻輸出了The Window, My Object
在作用域鏈中,執行函式時會建立一個稱為“執行期上下文(execution context)”的內部物件,執行期上下文定義了函式執行時的環境。
因為函式在全域性作用域中被
object.getNameFunc()
獨立呼叫,funAA的作用域鏈被初始化為undefined即window的[[Scope]]所包含的物件,導致輸出結果為window.name
對作用域鏈不是很瞭解的同學,可以檢視這邊文章【Javascript】深入理解javascript作用域與作用域鏈
實踐是檢驗真理的唯一標準,讓我們用程式碼測試一下
var name = "The Window"; var object = { name : "My Object", getNameFunc : function(){ return this.funAA(); }, funAA:function(){ return this.name } }; console.log(object.getNameFunc(),object.funAA())
可以發現,輸出了 My Object, My Object
getNameFunc仍為匿名函式,但是return的是this.funAA(),此時,this.funAA變成了由object呼叫,驗證了我們之前的猜想:
函式執行環境影響了this作用域,對這個demo的程式碼不太理解的同學,可以看一下另一個比較簡單的案例
this.x = 9; var module = { x: 81, getX: function() { console.log(this.x) } };module.getX(); // 81
var retrieveX = module.getX;
function A(){
this.x = 22;
retrieveX() //22
}
A()
new運算子對this作用域的影響
還是實踐出真理,我們先來寫一段程式碼
var a = 2 function test(){ this.a = 1 console.log(window.a) } new test() test()
可以看出輸出結果為2,1
new運算子改變了test函式內this的作用域,改變的原理是通過在函式內建立一個物件obj,並通過test.call(obj)
,執行obj.test()
,call函式原理:
Function.prototype.call1 = function(obj,...args){ obj.fn = this obj.fn(...args) delete obj.fn }
這樣test函式被物件obj呼叫,test複製的是obj的作用域鏈,而不是window
function subNew(){ var obj = {} var res = test.call(obj,...arguments) } subNew() // 作用等於new test()
let/var/const對this作用域的影響
繼續寫程式碼通過事實來說明
var a = 1 // 全域性作用域 let b = 1 // 塊級作用域 const c = 1 // 塊級作用域 function foo(){ var d = 1 // 函式作用域 this.a = 2 this.b = 2 this.c = 2 this.d = 2 console.log(a,b,c,d) // 2,1,1,1 } foo()
a為全域性作用域中的變數,可以被this物件訪問,b/c/d則不行
可以發現,全域性作用域中的a變數被改變,b變數與c變數都沒有被改變,說明在fn()中通過this訪問不到window作用域中的b/c變數
注:這裡說的訪問不到與const定義的變數是常量沒有關係,因為如果訪問到的話,是會報typeError的
箭頭函式對this作用域的影響
var num = 1 const object = { num:2, foo: function(){ return ()=>{ console.log(this.num) } } } object.foo() // 2
箭頭函式 this 指向 所處環境的上下文的 this 值,與是否獨立呼叫或作為屬性被呼叫,沒有關係。
箭頭函式沒有arguments
/prototype
,不能作為建構函式,不能使用new
總結
- this的指向取決於
函式執行時
所建立執行期上下文(execution context)
的內部物件,它與當前執行函式的[[scope]]所包含的物件組成了1個新的物件,這個物件就是活動物件
,然後此物件會被推入作用域鏈的前端 - 如果呼叫的函式,
被某一個物件所擁有
,那麼該函式在呼叫時,內部的this指向該物件。 - this指向與匿名函式沒有關係,如果函式在
全域性作用域window
中被獨立呼叫
,那麼該函式內部的this,則指向undefined。但是在非嚴格模式中,當this指向undefined時,它會被自動指向全域性物件。 - 在函式被獨立呼叫時,並處於
非嚴格模式
下,函式內的this物件有能力也僅能訪問到全域性作用域中定義的變數即window物件, 塊級作用域/函式作用域內的變數都無法被訪問 - 箭頭函式 this 指向
所處環境的上下文的 this 值
,與是否獨立呼叫或作為屬性被呼叫,沒有關係。
相關知識點
不理解new的實踐可以檢視我的這篇文章【Javascript】徹底捋清楚javascript中 new 運算子的實現
對作用域鏈不是很瞭解的同學,可以檢視這邊文章【Javascript】深入理解javascript作用域與作用域鏈