JavaScript作用域面試題避坑指南
導讀 | 以下是 5 種有趣的情況,其中 JavaScript 作用域的行為與你預期的不同。你可能會研究這些案例以提高對作用域的瞭解,或者只是為面試做準備。 |
在 JavaScript 中,程式碼塊、函式或模組為變數建立作用域。例如 if 程式碼塊為變數 message 建立作用域:
if (true) { const message = 'Hello'; console.log(message); // 'Hello' } console.log(message); // throws ReferenceError
在 if 程式碼塊作用域內可以訪問 message。但是在作用域之外,該變數不可訪問。
以下是 5 種有趣的情況,其中 JavaScript 作用域的行為與你預期的不同。你可能會研究這些案例以提高對作用域的瞭解,或者只是為面試做準備。
思考以下程式碼片段:
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 變數時會發生什麼?
答案:
console.log(l) 輸出數字 3 ,而 console.log(i) 則丟擲 ReferenceError。
l 變數是使用 var 語句宣告的。你可能已經知道,var 變數僅受函式體作用域限制而並非程式碼塊。
相反,變數 i 使用 let 語句宣告。因為 let 變數是塊作用域的,所以 i 僅在 for 迴圈作用域內才可訪問。
修復:
把 l 宣告從 var l = colors.length 改為 const l = colors.length。現在變數 l 被封裝在 for 迴圈體內。
在以下程式碼段中:
// ES2015 env { function hello() { return 'Hello!'; } } hello(); // ???
呼叫 hello() 會怎樣?(程式碼段在 ES2015 環境中執行)
答案:
因為程式碼塊為函式宣告建立了作用域,所以在 ES2015 環境中呼叫 hello() 會引發 ReferenceError: hello is not defined。
有趣的是,在 ES2015 之前的環境中,在執行上述程式碼段時不會丟擲錯誤。
你可以在程式碼塊中匯入模組嗎?
if (true) { import { myFunc } from 'myModule'; // ??? myFunc(); }
答案:
上面的 將觸發錯誤:'import' and 'export' may only appear at the top-level。
你只能在模組檔案的最頂級作用域(也稱為模組作用域)中匯入模組。
修復:
始終從模組作用域匯入模組。另外一個好的做法是將 import 語句放在原始檔的開頭:
import { myFunc } from 'myModule'; if (true) { myFunc(); }
ES2015 的模組系統是靜態的。透過分析 JavaScript 原始碼而不是執行程式碼來確定模組的依賴關係。所以在程式碼塊或函式中不能包含 import 語句,因為它們是在執行時執行的。
思考以下函式:
let p = 1; function myFunc(pp = p + 1) { return p; } myFunc(); // ???
呼叫 myFunc() 會發生什麼?
答案:
當呼叫函式 myFunc() 時,將會引發錯誤:ReferenceError: Cannot access 'p' before initialization。
發生這種情況是因為函式的引數具有自己的作用域(與函式作用域分開)。引數 p = p + 1 等效於 let p = p + 1。
讓我們仔細看看 p = p + 1。
首先,定義變數 p。然後 JavaScript 嘗試評估預設值表示式 p + 1,但此時繫結 p 已經建立但尚未初始化(不能訪問外部作用域的變數 let p = 1)。因此丟擲一個錯誤,即在初始化之前訪問了 p。
修復:
為了解決這個問題,你可以重新命名變數 let p = 1 ,也可以重新命名功能引數 p = p + 1。
讓我們選擇重新命名函式引數:
let p = 1; function myFunc(q = p + 1) { return q; } myFunc(); // => 2
函式引數從 p 重新命名為 q。當呼叫 myFunc() 時,未指定引數,因此將引數 q 初始化為預設值 p + 1。為了評估 p +1,訪問外部作用域的變數 p:p +1 = 1 + 1 = 2。
以下程式碼在程式碼塊內定義了一個函式和一個類:
if (true) { function greet() { // function body } class Greeter { // class body } } greet(); // ??? new Greeter(); // ???
是否可以在塊作用域之外訪問 greet 和 Greeter?(考慮 ES2015 環境)
答案:
function 和 class 宣告都是塊作用域的。所以在程式碼塊作用域外呼叫函式 greet() 和建構函式 new Greeter() 就會丟擲 ReferenceError。
必須注意 var 變數,因為它們是函式作用域的,即使是在程式碼塊中定義的。
由於 ES2015 模組系統是靜態的,因此你必須在模組作用域內使用 import 語法(以及 export)。
函式引數具有其作用域。設定預設引數值時,請確保預設表示式內的變數已經用值初始化。
在 ES2015 執行時環境中,函式和類宣告是塊作用域的。但是在 ES2015 之前的環境中,函式宣告僅在函式作用域內。
希望這些陷阱能夠幫你鞏固作用域知識!
原文來自:
來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/69955379/viewspace-2781893/,如需轉載,請註明出處,否則將追究法律責任。
相關文章
- 上雲避坑指南
- .NET AsyncLocal 避坑指南
- 坑爹的Python陷阱(避坑指南)Python
- Redis 安裝避坑指南Redis
- eBPF編寫避坑指南eBPF
- React Hooks使用避坑指南ReactHook
- 避坑指南:關於SPDK問題分析過程
- Apple Search Ads避坑指南:核心問題解析答疑APP
- JavaScript 作用域 與 作用域鏈JavaScript
- javaScript 作用域JavaScript
- JavaScript作用域JavaScript
- MySQL 之 LEFT JOIN 避坑指南MySql
- Shell 指令碼避坑指南(一)指令碼
- javascript之作用域與作用域鏈JavaScript
- Flutter beta3 避坑指南1Flutter
- TensorFlow-GPU安裝避坑指南GPU
- Mac 安裝 Hadoop 教程【避坑指南】MacHadoop
- 微信支付開發避坑指南
- Docker安裝flink及避坑指南Docker
- JavaScript面試題JavaScript面試題
- 深入理解JavaScript作用域和作用域鏈JavaScript
- H2 資料庫避坑指南資料庫
- 程式設計師避坑指南36條程式設計師
- JavaScript之作用域鏈JavaScript
- 學習JavaScript作用域JavaScript
- JavaScript 塊級作用域JavaScript
- JavaScript深度理解——作用域JavaScript
- javascript 詞法作用域JavaScript
- 淺談JavaScript作用域JavaScript
- 圖解javascript作用域圖解JavaScript
- 攻破javascript面試的完美指南【譯】JavaScript面試
- JavaScript面試題整理JavaScript面試題
- 【Java面試】 Javascript常見面試題!JavaScript面試題
- 【譯】學習JavaScript中提升、作用域、閉包的終極指南JavaScript
- 避坑指南:Golang框架自動化測試中的常見問題與解決方案大全Golang框架
- MongoDB 最佳實踐和場景避坑指南MongoDB
- 微服務進階之路 容器落地避坑指南微服務
- Canal v1.1.4版本避坑指南