上一篇文章中分析了 JS 中的數據類型和變量。這一篇文章將分析作用域,以及回答上一篇文章中變量提升的原因。
什么是作用域
作用域是一套規則,保存著變量,等待被引擎所查找。
var a = 1;
console.log(a); // => 1
console.log(b); // => ReferenceError
當打印 a 時,引擎就去作用域中查找 a,找到把結果返回。如果查找失敗,那么就會報錯。
詞法作用域
JS 采用的詞法作用域,也可以說是靜態作用域。簡單來說,詞法作用域是由寫代碼時將變量寫在哪里決定的。
先看一段代碼:
var a = 1;function fn() {var a = 2;return a;
}fn(); // => 2
當執行函數 fn
時,會返回 2,而不是 1。
作用域查找
JS 引擎會進行兩種查找,LHS 和 RHS。怎么理解?L 和 R 可以說代表左和右。什么的左和右?賦值操作的。
這里的賦值操作不一定出現 =
,比如參數傳遞也是一個賦值操作。
當變量出現在賦值操作的左邊時,引擎就會對這個變量進行 LHS 查找;當出現在右邊時(這個還可以理解為取得變量的源值),就會進行 RHS 查找。
function foo(a) {console.log(a);
}foo(2);
對于變量 a 來說,引擎會進行兩次查找,1 次 LHS,1 次 RHS。
調用 foo(),并傳入參數 2,這時存在著一個賦值操作即 a = 2
,進行一次 LHS 查找。打印 a 時,需要獲取 a 的源值,所以進行一次 RHS 查找。
如果查詢失敗呢?
對于 LHS 來說,給未聲明的賦值就會查詢失敗。
a = 2;
但是我們知道,上面的代碼在非嚴格模式下并不會報錯,而變量 a 會被自動創建。
而對于 RHS 來說,直接使用未聲明的變量就會報 ReferenceError。
console.log(a); // => ReferenceError
另外,RHS 雖然查詢成功,但是卻對查詢結果進行非法操作,就會報 TypeError。
var foo = 1;
foo(); // => TypeError
作用域鏈
前面說,作用域是根據名稱查找變量的一套規則。而在實際情況中,經常出現多個作用域嵌套的情況。
function foo(a) {console.log(a + b);
}
var b = 2;
foo(2); // => 4
當引擎對 b 進行 RHS 查找時,在當前作用域無法找到,引擎就會在外層作用域中查找,直到找到這個變量,或者直到抵達最外層作用域(全局作用域)為止。
LHS 查找也是如此。
把這樣一層一層嵌套的作用域,叫做作用域鏈。
函數作用域
函數作用域是指,屬于這個函數的全部變量都可以在這個函數的范圍內使用及復用。
function foo() {var a = 1;
}console.log(a); // => ReferenceError
也就是說,函數外部將無法訪問函數內部的變量。
但是這卻是非常有用的。我們可以利用函數隱藏內部實現,使其外部無法訪問、修改等。
立即執行函數表達式
利用函數作用域,可以將外部作用域無法訪問的內容包裝起來。但是,帶來了額外的一個問題,函數名本身“污染”了所在的作用域。
這時,就提出了 IIFE(立即執行函數表達式)。
(function foo() {// ...
}());
即包裝了內部函數,又避免了引入函數名。因為這個函數名無法被外部作用域所訪問。
IIFE 的進階用法是給其傳入參數:
(function fn(global) {// ...
})(window);
這樣的好處是可以縮短查詢時的作用域鏈。
塊作用域
ES6,通過 let 和 const 引入了塊作用域。
if (true) {let a = 1;
}
console.log(a); // => ReferenceError
變量提升
上一篇文章中中提到了變量提升。
在 JS 中,var a = 1;
這行代碼其實會被看成 var a
和 a = 2
,并在兩個階段去執行。
在編譯階段,執行聲明操作;在執行階段,執行賦值操作。
所有的變量聲明都會被提升到作用域的頂部,這個過程叫做“提升”。
函數聲明也會發生提升,并且函數聲明會先于變量提升:
var foo = 1;
function foo () {}typeof foo; // => 'number'
注意,只有函數聲明會被提升,而函數表達式不會被提升。
var foo = 1;
var foo = function () {}typeof foo; // => 'function'
小結
這篇文章梳理了 JavaScript 中作用域的基本知識。
接下來會介紹執行上下文和閉包這兩個概念,它們與作用域息息相關。
關于
這是我的公眾號,記錄著我的前端博客,沒事兒也分享一些電影、書籍。
歡迎一起交流學習。