別在不知道臨時死區的狀況下使用 JavaScript 變量

做者:Dmitri Pavlutin

翻譯:瘋狂的技術宅javascript

原文:https://dmitripavlutin.com/ja...前端

未經容許嚴禁轉載java

我問一個簡單的問題。如下哪一個代碼片斷將會產生錯誤?程序員

第一個建立實例,而後定義所用的類:面試

new Car('red'); // Does it work?

class Car {
  constructor(color) {
    this.color = color;
  }
}

第二個先調用而後定義函數:segmentfault

greet('World'); // Does it work?

function greet(who) {
  return `Hello, ${who}!`;
}

正確答案:第一個代碼段(帶有類)將生成 ReferenceError。第二個工做正常。服務器

若是你的答案與上述不一樣,或者在不知道底層發生了什麼的狀況下進行了猜想,那麼你須要掌握臨時死區(TDZ)。微信

TDZ 管理 letconstclass 語句的可用性。對於變量在 JavaScript 中的工做方式很是重要。多線程

1.什麼是臨時死區Temporal Dead Zone

讓咱們從一個簡單的 const 變量聲明開始。若是首先聲明並初始化變量,而後訪問它,那麼一切都會按預期進行:框架

const white = '#FFFFFF';
white; // => '#FFFFFF'

如今讓咱們試着在聲明以前訪問 white 變量:

white; // throws `ReferenceError`
const white = '#FFFFFF';
white;

在到 const white = '#FFFFFF' 語句的代碼行以前,變量 white 位於時間死區中。

在 TDZ 中訪問了 white 以後,JavaScript 會拋出 ReferenceError: Cannot access 'white' before initialization

image.png

TDZ(Temporal Dead Zone)語義禁止在聲明變量以前訪問變量。它強制執行紀律: 在聲明以前不要使用任何東西

2. 受 TDZ 影響的語句

讓咱們看看受 TDZ 影響的語句。

2.1 const 變量

正如你已經看到的,const 變量在 TDZ 中聲明和初始化行以前:

// Does not work!
pi; // throws `ReferenceError`

const pi = 3.14;

你必須在聲明後使用 const 變量:

const pi = 3.14;

// Works!
pi; // => 3.14

2.2 let 變量

在聲明行以前,let 聲明語句也會受到 TDZ 的影響:

// Does not work!
count; // throws `ReferenceError`

let count;

count = 10;

一樣,僅在聲明後使用 let 變量:

let count;

// Works!
count; // => undefined

count = 10;

// Works!
count; // => 10

2.3 class 語句

從簡介中能夠看出,在定義類以前不能使用它:

// Does not work!
const myNissan = new Car('red'); // throws `ReferenceError`

class Car {
  constructor(color) {
    this.color = color;
  }
}

爲了使它起做用,請在定義後保留類用法:

class Car {
  constructor(color) {
    this.color = color;
  }
}

// Works!
const myNissan = new Car('red');
myNissan.color; // => 'red'

2.4 constructor()內部的 super()

若是擴展父類,則在構造函數內部調用 super() 以前,this 綁定位於 TDZ 中:

class MuscleCar extends Car {
  constructor(color, power) {
    this.power = power;
    super(color);
  }
}

// Does not work!
const myCar = new MuscleCar('blue', '300HP'); // `ReferenceError`

constructor() 內部,this 在調用 super() 以前不能使用。

TDZ 建議調用父構造函數來初始化實例。完成以後,實例已準備就緒,你能夠在子構造函數中進行調整。

class MuscleCar extends Car {
  constructor(color, power) {
    super(color);
    this.power = power;
  }
}

// Works!
const myCar = new MuscleCar('blue', '300HP');
myCar.power; // => '300HP'

2.5 默認函數參數

默認參數存在於 intermidiate 做用域內,與全局做用域和函數做用域分開。默認參數還遵循 TDZ 限制:

const a = 2;
function square(a = a) {
  return a * a;
}
// Does not work!
square(); // throws `ReferenceError`

在聲明前,在表達式 a = a 的右側使用參數 a。這會產生關於 a 的引用錯誤。

要確保在聲明和初始化以後使用默認參數。讓咱們使用特殊的變量 init ,該變量在使用前已初始化:

const init = 2;
function square(a = init) {
  return a * a;
}
// Works!
square(); // => 4

3. varfunctionimport 語句

與前面相反,varfunction 的定義不受 TDZ 的影響。它們在當前做用域內被提高。

若是在聲明以前訪問 var 變量,則只會獲得 undefined

// Works, but don't do this!
value; // => undefined

var value;

However, a function can be used regarding where it is defined:
可是,可使用函數定義其位置:

// Works!
greet('World'); // => 'Hello, World!'

function greet(who) {
  return `Hello, ${who}!`;
}

// Works!
greet('Earth'); // => 'Hello, Earth!'

一般來講你對函數的實現不太感興趣,而只是想調用它。因此有時在定義函數以前先調用該函數是有意義的。

有趣的是, import 模塊也被提高:

// Works!
myFunction();

import { myFunction } from './myModule';

import 時,在 JavaScript 文件的開頭加載模塊的依賴項是一個好的作法。

4. TDZ 中的 typeof 行爲

typeof 運算符可用於肯定變量是否在當前做用域內定義。

例如,變量 notDefined 未定義,在這個變量上應用 typeof 運算符不會引起錯誤:

typeof notDefined; // => 'undefined'

因爲未定義變量,所以 typeof notDefined 的值爲 undefined

可是當與臨時死區中的變量一塊兒使用時,typeof 運算符有着不一樣的行爲。在這種狀況下,JavaScript 會報錯:

typeof variable; // throws `ReferenceError`

let variable;

這個引用錯誤背後的緣由是,你能夠靜態地(僅經過查看代碼便可)肯定已經定義了variable

5. TDZ 在當前做用域內運行

臨時死區會在存在聲明語句的做用域內影響變量。

image.png

讓咱們來看一個例子:

function doSomething(someVal) {
  // Function scope
  typeof variable; // => undefined
  if (someVal) {
    // Inner block scope
    typeof variable; // throws `ReferenceError`
    let variable;
  }
}
doSomething(true);

There are 2 scopes:
有 2 個做用域:

  1. 函數做用域
  2. 定義 let 變量的內部塊做用域

在函數做用域內,typeof variable 僅計算爲 undefined。在這裏, let variable 語句的 TDZ 無效。

在內部做用域中,在聲明以前使用變量的 typeof variable 語句引起錯誤ReferenceError: Cannot access 'variable' before initialization。 TDZ 僅存在於此內部做用域內。

6. 結論

TDZ 是一個重要概念,會影響 constletclass 語句的可用性。不容許在聲明前使用變量。

當你能夠在聲明以前使用 var 變量時,它們會繼承舊的行爲。你應該避免這樣作。

在我看來,當把良好的編碼實踐進入語言規範時,TDZ 就是其中的一個好東西。


本文首發微信公衆號:前端先鋒

歡迎掃描二維碼關注公衆號,天天都給你推送新鮮的前端技術文章

歡迎掃描二維碼關注公衆號,天天都給你推送新鮮的前端技術文章

歡迎繼續閱讀本專欄其它高贊文章:


相關文章
相關標籤/搜索