JavaScript 解釋上下文環境

Javascript中除了原型鏈,還有一重要知識環節 —— 上下文環境;通過對執行上下文環境的瞭解,能夠讓我們對Javascript內部原理有更深入的理解。

先以執行棧爲引入,讓大家感受一下:某個xxx.js代碼在執行時,執行棧是怎麼運作的,執行棧和上下文環境有什麼關係?
執行棧: 一種數據結構棧;在js代碼被執行時,已被創建的執行上下文按照被創建的順序依次被存儲(壓入)棧中。
【圖一】
在這裏插入圖片描述
【圖二】
在這裏插入圖片描述
依照上面圖一、圖二爲例子,解釋下執行棧的執行過程:
Js引擎執行到代碼文件js-test.js時,會創建全局執行上下文(Global Excution Context)並壓入棧中。對應圖二中第1幅圖的Global Excution Context被壓入棧;

當執行到圖一第14行代碼時,即執行到函數method_1th,會創建新的函數執行上下文並壓入棧中。對應圖二中第2幅圖的method_1th函數上下文環境(function method_1th() Excution Context)被壓入棧。

在執行函數method_1th時,執行到第6行代碼method_2th時,會創建當前函數的函數執行上下文並壓入棧中。對應圖二中第3幅圖的method_2th函數上下文環境(function method_2th() Excution Context)被壓入棧。

當執行到第7行代碼,函數method_2th已執行結束。則執行棧中頂層的method_2th函數上下文被彈出,此時執行控制流程從method_2th函數上下文環境切換到method_1th函數上下文環境。對應圖二的第4幅圖!

然後,函數method_1th執行結束,即執行到第15行代碼時,執行棧中頂層的method_1th函數上下文被彈出,此時執行控制流程從method_1th函數上下文環境切換到Global Excution Context全局執行上下文環境。

由此可見,Javascript中的全局執行上下文和函數執行上下文都是由Js引擎所創建。並且是在Js引擎執行過程中依次創建,而盛放這些上下文的數據結構叫執行棧。

好比Java語言中的Java虛擬機棧,每個方法被執行的同時,都會創建一個棧幀(Stack Frame)。而每一個方法從調用直到執行完成的過程,則對應着一個棧幀在虛擬機棧從入棧到出棧的過程!

下面則是執行上下文的分類:
在這裏插入圖片描述

下面則是創建執行上下文的過程:
在這裏插入圖片描述
結合上圖的思維導圖,通過代碼環境逐個分析他們的創建過程:
關於 this 的綁定
在這裏插入圖片描述
從執行棧的執行上來解釋,在執行第9行代碼那一刻之前,全局上下文環境已經創建且被壓入了執行棧,此時環境中的this指向全局對象。

當在第9行代碼執行時,myFunction函數上下文被創建並壓入執行棧中(partialFunc函數定義在了myFunction函數中,不會被執行)且處於棧頂層,此時執行控制流程處在myFunction函數上下文環境中。此時函數partialFunc中的this指向myFunctioin(結合創建執行上下文的過程圖可知,this的指向取決於函數是怎麼被調用的)。

當執行到第15行代碼時,新的函數執行上下文被創建並壓入棧中,即partialFunc函數執行上下文。此時執行控制流程處在partialFunc函數上下文環境中。由於this的指向取決於函數是怎麼被調用的,而當前函數的執行沒有指定任何引用對象,所以當前函數上下文環境的this指向全局對象。

通過對this綁定的分析之後,生出這個疑問:
常用方法call() 和 apply() 中的 this 是綁定到什麼對象上?
方法call() 和 apply() 被調用時,即被調用的方式是 this.方法名.call(參數1, 參數2) this.方法名.apply(參數1, 參數2)

如果不關注.call() 和 .apply(),結合關於 this 的綁定進行分析。可以得出結論,此時的方法內部的this指向的是調用方法 (this.方法名)this

但是,如果方法call() 和 apply() 使用調用方式 —— this.方法名.call(參數1, 參數2) this.方法名.apply(參數1, 參數2) ,那麼旨在使用參數1來更改this(參數1來替換this),具體解釋可見下方代碼:
在這裏插入圖片描述
bind() 方法中的 this
ECMAScript 5 引入了 Function.prototype.bind。調用 f.bind(someObject) 會創建一個與 f 具有相同函數體和作用域的函數,但是在這個新函數中,this 將永久地被綁定到了 bind 的第一個參數,無論這個函數是如何被調用的。
因此bind方法在使用上和call、apply是一致的。只是bind是會新建一個新的函數!

關於詞法組件的創建
詞法組件的創建,這裏分爲在兩種條件環境下:1全局上下文環境; 2函數上下文環境;創建之後的結構如下圖:

在這裏插入圖片描述
由於變量環境和詞法環境相似,因此與詞法環境的環境結構式一致的。
下面通過變量環境細緻的瞭解下在上圖框架結構下的變量環境是如何的!~
關於變量環境組件的創建
在這裏插入圖片描述
結合上圖代碼,分析:
該變量環境組件的創建過程從代碼上來看,該js代碼文件中包含了兩類上下文環境 —— 因此需要創建全局上下文和函數上下文。
創建全局上下文環境
在這裏插入圖片描述
在全局上下文環境中 this的綁定指向全局對象;使用let、const修飾的變量a和b,以及函數multiply在詞法環境中被定義且未被初始化(uninitialized)
而使用var修飾的變量c,在變量環境中被定義且已被初始化(undefined)

創建函數上下文環境
在這裏插入圖片描述
從源代碼文件的第10行代碼 c = multiply(20, 30); 來看,函數上下文環境中this指向的也是全局對象。而在詞法環境中使用var修飾的變量g在變量環境中被定義且已被初始化(undefined)。函數上下文環境中的詞法環境中,被定義的變量則是函數的參數。

參考文章:
https://juejin.im/post/5b
https://juejin.im/post/585

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