1. 執行上下文環境及作用域
(1)執行上下文:執行上下文的定義---在執行代碼之前,把將要用到的變量都事先拿出來,有的直接賦值了(this和函數聲明),有的先用undefined占個位(變量和函數表達式)。
處於活動狀態的執行上下文環境只有一個。函數每被調用一次,都會產生一個新的執行上下文環境。當執行流進入一個函數時,這個函數的環境就會被推入一個環境棧中。在函數執行之后,環境棧將其環境彈出。即函數執行完之后就會將其執行上下文環境銷毀(還有種情況就是閉包,閉包執行完之后,他的執行上下文環境不會被銷毀)。
(2)全局執行環境 是最外圍的一個執行環境,在web瀏覽器中,全局執行環境是window對象。
(3)作用域:函數在定義的時候,就已經確定了函數體內部自由變量的作用域。js沒有塊及作用域,除了全局作用域外,只有函數才能創建作用域。作用域最大的用處就是隔離變量,不同作用域下同名變量不會有沖突。
(4)作用域鏈:作用域有上下級關系,上下級關系的確定就看函數在哪個作用域下創建的,當代碼在一個環境中執行,會創建變量對象的一個作用域鏈。當訪問變量時,會一級一級向上尋找變量定義,直到找到他。若一直尋找到全局作用域還找不到就會報 'xxx is not defined'的錯誤。
作用域鏈的用途是保證對執行環境有權訪問的所有變量和函數的有序訪問。例:
var num = 1; function changeNum(){ if(num===1){ // 可以在函數內部訪問num,是因為可以再作用域鏈中找到他 num=2; } else{ num=1; } } changNum(); alert(num) // 2
局部環境中開始時會先在自己的變量對象中搜索變量名和函數名,搜索不到的話就搜索上一級作用域鏈。
2. 延長作用域鏈
try catch語句的catch塊;with語句,這兩個語句會在作用域鏈的前端添加一個變量對象。
function buildUrl() { var qs = "?debug=true"; with(location){ var url = href + qs; } return url; }
with 語句接收的是 location 對象,因此其變量對象中就包含了 location 對象的所有屬性和方法,而這個變量對象被添加到了作用域鏈的前端。
with 語句中引用變量 href 時(實際引用的是 location.href),with 語句內部,定義了一個名為 url 的變量,因而 url 就成了函數執行環境的一部分,所以可以作為函數的值被返回。
3. 沒有塊級作用域
js有作用域,函數作用域,但是沒有塊級作用域(ES6新增了塊級作用域 let const 聲明)。js除了全局作用域外,只有函數能創建作用域
if 語句 for語句 中的變量聲明會將變量添加到當前的執行環境
(1)聲明變量
使用 var 聲明的變量會自動被添加到最接近的環境中。如果初始化變量,沒有用var聲明,該變量會自動被添加到全局環境中。
function add(num1, num2) { var sum = num1 + num2; // 這里如果有var聲明則是被添加到函數內部執行環境中,所以后面訪問不到 return sum; } var result = add(10, 20); //30 alert(sum); //由於 sum 不是有效的變量,因此會導致錯誤 function add(num1, num2) { sum = num1 + num2; // 這里沒有用var聲明,sum變量被添加到全局作用域,所以后面可以訪問到sum的值 return sum; } var result = add(10, 20); //30 alert(sum); //30
(2)查詢標識符
當在某個環境中為了讀取或寫入而引用一個標識符時,必須通過搜索來確定該標識符實際代表什么。搜索過程從作用域鏈的前端開始,向上逐級查詢與給定名字匹配的標識符。如果在局部環境中找到了該標識符,搜索過程停止,變量就緒。如果在局部環境中沒有找到該變量名,則繼續沿作用域鏈向上搜索。搜索過程將一直追溯到全局環境的變量對象。如果在全局環境中也沒有找到這個標識符,則意味着該變量尚未聲明。
