深刻理解javascript系列之執行環境

引言

當javascript代碼執行從一個函數進入到另外一個函數的時候,語言在實現上爲當前執行函數保存外部的執行環境(變量),在當前函數進行變量標識符查找的時候,查找的規則是首先在當前的執行環境中查找對應的變量,而後逐步從上級的執行環境中查找.這種對變量實現的存儲和查找機制就是javascript中的做用鏈域.下面先從一些js執行環境的基礎知識提及,而後從ECMA的規範上理解javascript的執行環境.javascript

基礎知識

執行棧

執行棧是存儲javascript執行上下文的一種結構,它具備先入先出的特色.javascript在執行的時候會建立全局的執行上下文.在執行到函數代碼的時候,會建立新的執行上下文,執行完對應函數的時候,會退出當前的上下文回到以前的執行上下文繼續代碼的執行.html

聲明提高

javascript在建立執行上下文的時候,會對當前執行環境聲明的變量進行綁定(初始化存儲位置),這個在必定程度上也解決了函數聲明的前後順序問題,下面這段代碼是能夠正常執行的java

console.log(a);// undefined
var a = 0;// let const與var的區別是在建立對應的以後環境的時候是let const不會爲變量綁定初始值,
var會綁定初始值(undefined),引用一個沒有初始值的變量會報錯
test();
function test() { test2(); }
function test2() { console.log(1); }複製代碼

從ECMA規範理解js執行環境

在javascript進入到函數的執行代碼的時候,會建立新的執行上下文,將當前的上下文推入執行棧進行代碼的執行.下面先簡單的理解執行上下文的基本組件:bash

Execution Contexts = {  
  code evaluation state // 代碼執行的狀態 用戶代碼的執行暫停和恢復
  Realm // realm是對javascript執行邊界的一些限制
  LexicalEnvironment:{    
    this  // 會進行this的bind  理解this是當前函數的caller
    Environment Record  // 用於初始化和存儲當前上下文聲明的函數聲明,變量 
    outer LexicalEnvironment  // 用於從外部的做用域查找標識符(做用域鏈)  } 
    VariableEnvironment: {} // VariableEnvironment和LexicalEnvironment
   是類似的概念下面會單獨進行講解
}複製代碼

this

this是指調用函數的caller.在進入函數執行的時候會建立新的執行上下文而且對this進行綁定(箭頭函數使用的是Lexical this,即這個函數被建立時的this就是函數內部的this).閉包

如何肯定this

const obj = {  
  name: 100, 
  test: function() {    console.log(this.name);  }
};
obj.test(); // 100
const obj2 = {  
    name: 200,
    test: () => {    
     console.log(this.name);  // 這段代碼的執行環境是全局的環境
    因此箭頭函數中this的指向是window  
    }
}
obj2.test(); // undefined 複製代碼

上面的代碼塊中obj是一個引用類型,在ECMA規範中有引用類型的定義,能夠理解成下面的形式app

Reference {  
  the base value component // 引用類型的值 對於上面的例子來講就是obj自己
  the referenced name component // 引用類型的名字  
                      // the Boolean-valued strict reference flag
} 複製代碼

在執行上下文中確認this的指向可使用以下的規則: ecmascript


理解VariableEnvironment和LexicalEnvironment

VariableEnvironment是建立執行上下文的時候進行變量的初始化綁定和存儲,LexicalEnvironment(LexicalEnvironment在進行變量初始化後會複製一份VariableEnvironment)主要用於在代碼執行階段對標識符的解析而且隨着代碼執行(例如產生with語句)會建立新的LexicalEnvironment到當前的LexicalEnvironment以前.能夠經過下面的例子來加深對上面例子的理解:
function test() {      var a = 10      var obj = {a:20}      with(obj) {        var test2 = function() {          console.log(a)        }        function test3() {          console.log(a)        }      }      return {test2,test3}    }    var hah = test()    hah.test2() //log 20    hah.test3()//log 20 複製代碼

理解閉包

當前的函數存在對外部做用域變量的訪問會造成閉包.閉包保存的是生成閉包時候的執行上下文的LexicalEnvironment. (Closure is when a function remembers and accesses variables from outside of its own scope, even when that function is executed in a different scope.)ide

參考

VariableEnvironment和LexicalEnvironment的區別函數

lexical-environments-ecmascript-implementationui

ECMAScript2017

how-to-understand-js-realms

相關文章
相關標籤/搜索