首頁 >web前端 >js教程 >5個關於JS作用域的陷阱(總結)

5個關於JS作用域的陷阱(總結)

青灯夜游
青灯夜游轉載
2020-12-25 17:32:065155瀏覽

5個關於JS作用域的陷阱(總結)

在 JavaScript 中,程式碼區塊、函數或模組會為變數建立作用域。例如if 程式碼區塊為變數message 建立作用域:

if (true) {
  const message = 'Hello';
  console.log(message); // 'Hello'
}
console.log(message); // throws ReferenceError

if 程式碼區塊作用域內可以存取message。但是在作用域之外,該變數不可存取。

好的,這是作用域的簡短介紹。如果你想了解更多信息,建議閱讀我的文章用簡單的詞解釋 JavaScript 作用域

以下是 5 種有趣的情況,其中 JavaScript 作用域的行為與你預期的不同。你可能會研究這些案例以提高對作用域的了解,或只是為面試做準備。

1. for 迴圈內的var 變數

思考以下程式碼片段:

const colors = ['red', 'blue', 'white'];

for (let i = 0, var l = colors.length; i < l; i++) {
  console.log(colors[i]); // &#39;red&#39;, &#39;blue&#39;, &#39;white&#39;
}
console.log(l); // ???
console.log(i); // ???

當你列印li 變數時會發生什麼?

答案

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 迴圈體內。

2. 程式碼區塊中的函數宣告

在下列程式碼片段:

// ES2015 env
{
  function hello() {
    return &#39;Hello!&#39;;
  }
}

hello(); // ???

呼叫hello() 會怎樣? (程式碼片段在ES2015 環境中執行)

答案

因為程式碼區塊為函數宣告建立了作用域,所以在ES2015 環境中呼叫hello() 會引發 ReferenceError: hello is not defined

有趣的是,在 ES2015 之前的環境中,執行上述程式碼片段時不會拋出錯誤。 你知道為什麼嗎?請在下面的評論中寫下你的答案!

3. 你可以在哪裡導入模組?

你可以在程式碼區塊中導入模組嗎?

if (true) {
  import { myFunc } from &#39;myModule&#39;; // ???
  myFunc();
}

答案

上面的腳本將觸發錯誤: 'import' and 'export' may only appear at the top-level

你只能在模組檔案的最頂級作用域(也稱為模組作用域)中導入模組。

修正

總是從模組作用域匯入模組。另外一個好的做法是將 import 語句放在原始檔的開頭:

import { myFunc } from &#39;myModule&#39;;

if (true) {
  myFunc();
}

ES2015 的模組系統是靜態的。透過分析 JavaScript 原始碼而不是執行程式碼來確定模組的依賴關係。所以在程式碼區塊或函數中不能包含 import 語句,因為它們是在執行時執行的。

4. 函數參數作用域

思考以下函數:

let p = 1;

function myFunc(p = 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,存取外部作用域的變數 pp 1 = 1 1 = 2

5. 函数声明与类声明

以下代码在代码块内定义了一个函数和一个类:

if (true) {
  function greet() {
    // function body
  }

  class Greeter {
    // class body
  }
}

greet();       // ???
new Greeter(); // ???

是否可以在块作用域之外访问 greetGreeter(考虑 ES2015 环境)

答案

functionclass 声明都是块作用域的。所以在代码块作用域外调用函数 greet() 和构造函数 new Greeter() 就会抛出 ReferenceError

6. 总结

必须注意 var 变量,因为它们是函数作用域的,即使是在代码块中定义的。

由于 ES2015 模块系统是静态的,因此你必须在模块作用域内使用 import 语法(以及 export)。

函数参数具有其作用域。设置默认参数值时,请确保默认表达式内的变量已经用值初始化。

在 ES2015 运行时环境中,函数和类声明是块作用域的。但是在 ES2015 之前的环境中,函数声明仅在函数作用域内。

希望这些陷阱能够帮你巩固作用域知识!

英文原文地址:https://dmitripavlutin.com/javascript-scope-gotchas/

作者:Dmitri Pavlutin

更多编程相关知识,请访问:编程入门!!

以上是5個關於JS作用域的陷阱(總結)的詳細內容。更多資訊請關注PHP中文網其他相關文章!

陳述:
本文轉載於:segmentfault.com。如有侵權,請聯絡admin@php.cn刪除