【Javascript】深入理解this作用域問題以及new/let/var/const對this作用域的影響

理解this作用域

《javascript高級程序設計》中有說到:

this對象是在運行時基於函數的執行環境綁定的:在全局函數中,this等於window,而當函數被作爲某個對象調用時,this等於那個對象。不過,匿名函數具有全局性,因此this對象同常指向window

針對於匿名函數this具有全局性的觀點仍是有爭議的,可參考 https://www.zhihu.com/questio...

關於閉包經常會看到這麼一道題:

var name = "The Window";
    var object = {
        name : "My Object",
        getNameFunc : function(){
            return function(){
                return this.name;
            };
        }
    };
console.log(object.getNameFunc()());//result:The Window


<font color="green">在這裏,getNameFunc return了1個匿名函數,可能你會認爲這就是輸出值爲<font color="blue">The Window</font>的原因 </font>

但是,我們再來嘗試寫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())


可以發現,同樣是匿名函數,卻輸出了<font color="blue">The Window, My Object</font>

在作用域鏈中,執行函數時會創建一個稱爲“運行期上下文(execution context)”的內部對象,運行期上下文定義了函數執行時的環境。

個人認爲是因爲<font color="red">函數執行在window作用域</font>下,getNameFunc的作用域鏈被初始化爲<font color="red">window的[[Scope]]</font>所包含的對象,導致輸出結果爲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())


可以發現,輸出了 <font color="blue">My Object, My Object</font>
getNameFunc仍爲匿名函數,但是return的是this.funAA(),此時,this.funAA變成了在getNameFunc作用域中執行,而不是在window下,驗證了我們之前的猜想:

函數執行環境影響了this作用域

new運算符對this作用域對影響

還是實踐出真理,我們先來寫一段代碼

var a = 2
function test(){
    this.a = 1
    console.log(window.a)
}
new test()
test()


可以看出輸出結果爲<font color="blue">2,1</font>
new運算符改變了test函數內this的志向,改變的原理是通過在函數內創建一個對象obj,並將test內this作用域指向了obj
類似於

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 fn(){
    this.a = 2
    this.b = 2
    this.c =2
    console.log(this.a,this.b,this.c)
    console.log(a,b,c)
    //this指向全局作用域  this === window //true
}
fn()

var聲明的變量屬於函數作用域,let/const聲明的變量屬於塊級作用域

可以發現,全局作用域中的a變量被改變,b變量與c變量都沒有被改變,說明在fn()中通過this訪問不到window作用域中的b/c變量
<font color="red">注:這裏說的訪問不到與const定義的變量是常量沒有關係,因爲如果訪問到的話,是會報typeError的</font>

總結

1.this的指向取決於<font color="red">函數執行時</font>所創建運行期上下文(execution context)的內部對象,它會複製父函數的作用域鏈,再在前端插入自己的活動對象,與是否是匿名函數無關
2.在作用域鏈中,var定義的變量屬於函數作用域,可以被子級作用域下的this訪問,而let/const定義的變量數據塊級作用域,不允許在其子級作用域中被訪問

相關知識點

不理解new的實踐可以查看我的這篇文章【Javascript】徹底捋清楚javascript中 new 運算符的實現
對作用域鏈不是很瞭解的同學,可以查看這邊文章【Javascript】深入理解javascript作用域與作用域鏈

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章