新聞中心
在 JavaScript 中,代碼塊、函數(shù)或模塊為變量創(chuàng)建作用域。例如 if 代碼塊為變量 message 創(chuàng)建作用域:

公司主營業(yè)務:做網(wǎng)站、成都網(wǎng)站建設、移動網(wǎng)站開發(fā)等業(yè)務。幫助企業(yè)客戶真正實現(xiàn)互聯(lián)網(wǎng)宣傳,提高企業(yè)的競爭能力。成都創(chuàng)新互聯(lián)是一支青春激揚、勤奮敬業(yè)、活力青春激揚、勤奮敬業(yè)、活力澎湃、和諧高效的團隊。公司秉承以“開放、自由、嚴謹、自律”為核心的企業(yè)文化,感謝他們對我們的高要求,感謝他們從不同領域給我們帶來的挑戰(zhàn),讓我們激情的團隊有機會用頭腦與智慧不斷的給客戶帶來驚喜。成都創(chuàng)新互聯(lián)推出諸城免費做網(wǎng)站回饋大家。
- if (true) {
- const message = 'Hello';
- console.log(message); // 'Hello'
- }
- console.log(message); // throws ReferenceError
在 if 代碼塊作用域內可以訪問 message。但是在作用域之外,該變量不可訪問。
以下是 5 種有趣的情況,其中 JavaScript 作用域的行為與你預期的不同。你可能會研究這些案例以提高對作用域的了解,或者只是為面試做準備。
1. for 循環(huán)內的 var 變量
思考以下代碼片段:
- const colors = ['red', 'blue', 'white'];
- for (let i = 0, var l = colors.length; i < l; i++) {
- console.log(colors[i]); // 'red', 'blue', 'white'
- }
- console.log(l); // ???
- console.log(i); // ???
當你打印 l 和 i 變量時會發(fā)生什么?
答案:
console.log(l) 輸出數(shù)字 3 ,而 console.log(i) 則拋出 ReferenceError。
l 變量是使用 var 語句聲明的。你可能已經(jīng)知道,var 變量僅受函數(shù)體作用域限制而并非代碼塊。
相反,變量 i 使用 let 語句聲明。因為 let 變量是塊作用域的,所以 i 僅在 for 循環(huán)作用域內才可訪問。
修復:
把 l 聲明從 var l = colors.length 改為 const l = colors.length?,F(xiàn)在變量 l 被封裝在 for 循環(huán)體內。
2. 代碼塊中的函數(shù)聲明
在以下代碼段中:
- // ES2015 env
- {
- function hello() {
- return 'Hello!';
- }
- }
- hello(); // ???
調用 hello() 會怎樣?(代碼段在 ES2015 環(huán)境中執(zhí)行)
答案:
因為代碼塊為函數(shù)聲明創(chuàng)建了作用域,所以在 ES2015 環(huán)境中調用 hello() 會引發(fā) ReferenceError: hello is not defined。
有趣的是,在 ES2015 之前的環(huán)境中,在執(zhí)行上述代碼段時不會拋出錯誤。
3. 你可以在哪里導入模塊?
你可以在代碼塊中導入模塊嗎?
- if (true) {
- import { myFunc } from 'myModule'; // ???
- myFunc();
- }
答案:
上面的腳本將觸發(fā)錯誤:'import' and 'export' may only appear at the top-level。
你只能在模塊文件的最頂級作用域(也稱為模塊作用域)中導入模塊。
修復:
始終從模塊作用域導入模塊。另外一個好的做法是將 import 語句放在源文件的開頭:
- import { myFunc } from 'myModule';
- if (true) {
- myFunc();
- }
ES2015 的模塊系統(tǒng)是靜態(tài)的。通過分析 JavaScript 源代碼而不是執(zhí)行代碼來確定模塊的依賴關系。所以在代碼塊或函數(shù)中不能包含 import 語句,因為它們是在運行時執(zhí)行的。
4. 函數(shù)參數(shù)作用域
思考以下函數(shù):
- let p = 1;
- function myFunc(pp = p + 1) {
- return p;
- }
- myFunc(); // ???
調用 myFunc() 會發(fā)生什么?
答案:
當調用函數(shù) myFunc() 時,將會引發(fā)錯誤:ReferenceError: Cannot access 'p' before initialization。
發(fā)生這種情況是因為函數(shù)的參數(shù)具有自己的作用域(與函數(shù)作用域分開)。參數(shù) p = p + 1 等效于 let p = p + 1。
讓我們仔細看看 p = p + 1。
首先,定義變量 p。然后 JavaScript 嘗試評估默認值表達式 p + 1,但此時綁定 p 已經(jīng)創(chuàng)建但尚未初始化(不能訪問外部作用域的變量 let p = 1)。因此拋出一個錯誤,即在初始化之前訪問了 p。
修復:
為了解決這個問題,你可以重命名變量 let p = 1 ,也可以重命名功能參數(shù) p = p + 1。
讓我們選擇重命名函數(shù)參數(shù):
- let p = 1;
- function myFunc(q = p + 1) {
- return q;
- }
- myFunc(); // => 2
函數(shù)參數(shù)從 p 重命名為 q。當調用 myFunc() 時,未指定參數(shù),因此將參數(shù) q 初始化為默認值 p + 1。為了評估 p +1,訪問外部作用域的變量 p:p +1 = 1 + 1 = 2。
5. 函數(shù)聲明與類聲明
以下代碼在代碼塊內定義了一個函數(shù)和一個類:
- if (true) {
- function greet() {
- // function body
- }
- class Greeter {
- // class body
- }
- }
- greet(); // ???
- new Greeter(); // ???
是否可以在塊作用域之外訪問 greet 和 Greeter?(考慮 ES2015 環(huán)境)
答案:
function 和 class 聲明都是塊作用域的。所以在代碼塊作用域外調用函數(shù) greet() 和構造函數(shù) new Greeter() 就會拋出 ReferenceError。
6. 總結
必須注意 var 變量,因為它們是函數(shù)作用域的,即使是在代碼塊中定義的。
由于 ES2015 模塊系統(tǒng)是靜態(tài)的,因此你必須在模塊作用域內使用 import 語法(以及 export)。
函數(shù)參數(shù)具有其作用域。設置默認參數(shù)值時,請確保默認表達式內的變量已經(jīng)用值初始化。
在 ES2015 運行時環(huán)境中,函數(shù)和類聲明是塊作用域的。但是在 ES2015 之前的環(huán)境中,函數(shù)聲明僅在函數(shù)作用域內。
希望這些陷阱能夠幫你鞏固作用域知識!
當前文章:面試中關于 JavaScript 作用域的 5 個坑
轉載來于:http://www.dlmjj.cn/article/dhcheip.html


咨詢
建站咨詢
