執行環境分紅兩種類型。html
全局執行環境
是最外層的執行環境,在瀏覽器環境中,就是window對象。前端
函數執行環境
每一個函數都有本身的執行環境,每進入一個函數時,函數的執行環境就被推到一個環境棧中。在函數執行完之後,這個執行環境會彈棧。瀏覽器
與全局執行環境、函數執行環境的對應的,這裏有全局做用域、局部做用域的概念。閉包
其實,全局與局部做用域的訪問權限,是由做用域鏈決定的。
爲了搜索變量、函數,每進入一個新的執行環境都會建立一個做用域鏈。做用域鏈會保存,函數定義環境的(也就是當前函數的外層)有權訪問的的變量和函數,
以下圖所示。函數
做用域鏈的最前端始終是當前執行的代碼所在環境的變量對象(若是該環境是函數,則將其活動對象做爲變量對象),下一個變量對象來自包含環境(包含當前還行環境的環境),下一個變量對象來自包含環境的包含環境,依次往上,直到全局執行環境的變量對象。全局執行環境的變量對象始終是做用域鏈中的最後一個對象。3d
標識符解析是沿着做用域一級一級的向上搜索標識符的過程。搜索過程始終是從做用域的前端逐地向後回溯,直到找到標識符(找不到,就會致使錯誤發生)。code
函數的局部環境能夠訪問函數做用域中的變量,也能夠訪問和操做父環境(包含環境)乃至全局環境中的變量。htm
父環境只能訪問包含其的環境和本身環境中的變量和函數,不能訪問其子環境中的變量和函數。對象
全局環境只能訪問全局環境中的變量和函數,不能直接訪問局部環境中的任何數據。blog
其實,函數的局部環境能夠訪問函數做用域中的變量,就是閉包。
function a(){ let xxx = 1; log(); function log(){ console.log(xxx); } }
var a = 1; c(); function c(){ console.log(a); // undefined var a = 2; }
解析器在函數執行環境中發現變量a,所以再也不上層查找。可是console.log(a)時還未賦值,因此打印undefined。上面的代碼等價於:
var a = 1; c(); function c(){ var a; console.log(a); // undefined a = 2; }
這種現象就是變量提高。
注意,上面這段代碼若是var改爲let。
let a = 1; c(); function c(){ console.log(a); // 報錯 let a = 2; }
此時,會造成暫時性死區,let在預解析過程當中不會被提高。
c() function c(){ //... }
函數在預解析過程當中也會發生提高現象。
注意:只有函數聲明形式才能被提高!!
c(); //會報錯 var c = function(){ }