深入理解JS:var、let、const的異同_網頁設計公司

網頁設計公司推薦不同的風格,搶佔消費者視覺第一線

透過選單樣式的調整、圖片的縮放比例、文字的放大及段落的排版對應來給使用者最佳的瀏覽體驗,所以不用擔心有手機版網站兩個後台的問題,而視覺效果也是透過我們前端設計師優秀的空間比例設計,不會因為畫面變大變小而影響到整體視覺的美感。

目錄

  • 序言
  • var 與 let 的區別
    • 作用域
    • 重複聲明
    • 綁定全局對象
    • 變量提升與暫存死區
  • let 與 const 異同
  • 參考

1.序言

var、let 和 const 都是 JavaScript 中用來聲明變量的關鍵字,並且 let 和 const 關鍵字是在 ES6 中才新增的。既然都是用來聲明變量的,那它們之間有什麼區別呢?讓我們來一探究竟。

2.var 與 let 的區別

(1)作用域

用 var 聲明的變量的作用域是它當前的執行上下文,即如果是在任何函數外面,則是全局執行上下文,如果在函數裏面,則是當前函數執行上下文。換句話說,var 聲明的變量的作用域只能是全局或者整個函數塊的。

而 let 聲明的變量的作用域則是它當前所處代碼塊,即它的作用域既可以是全局或者整個函數塊,也可以是 if、while、switch等用{}限定的代碼塊。

另外,var 和 let 的作用域規則都是一樣的,其聲明的變量只在其聲明的塊或子塊中可用。

示例代碼:

function varTest() {
  var a = 1;

  {
    var a = 2; // 函數塊中,同一個變量
    console.log(a); // 2
  }

  console.log(a); // 2
}

function letTest() {
  let a = 1;

  {
    let a = 2; // 代碼塊中,新的變量
    console.log(a); // 2
  }

  console.log(a); // 1
}

varTest();
letTest();

從上述示例中可以看出,let 聲明的變量的作用域可以比 var 聲明的變量的作用域有更小的限定範圍,更具靈活。

(2)重複聲明

var 允許在同一作用域中重複聲明,而 let 不允許在同一作用域中重複聲明,否則將拋出異常。

var 相關示例代碼:

var a = 1;
var a = 2;

console.log(a) // 2

function test() {
  var a = 3;
  var a = 4;
  console.log(a) // 4
}

test()

let 相關示例代碼:

if(false) {
  let a = 1;
  let a = 2; // SyntaxError: Identifier 'a' has already been declared
}
switch(index) {
  case 0:
    let a = 1;
  break;

  default:
    let a = 2; // SyntaxError: Identifier 'a' has already been declared
    break;
}

從上述示例中可以看出,let 聲明的重複性檢查是發生在詞法分析階段,也就是在代碼正式開始執行之前就會進行檢查。

(3)綁定全局對象

var 在全局環境聲明變量,會在全局對象里新建一個屬性,而 let 在全局環境聲明變量,則不會在全局對象里新建一個屬性。

示例代碼:

var foo = 'global'
let bar = 'global'

console.log(this.foo) // global
console.log(this.bar) // undefined

那這裏就一個疑問, let 在全局環境聲明變量不在全局對象的屬性中,那它是保存在哪的呢?

var foo = 'global'
let bar = 'global'

function test() {}

console.dir(test)

在Chrome瀏覽器的控制台中,通過執行上述代碼,查看 test 函數的作用域鏈,其結果如圖:

由上圖可知,let 在全局環境聲明變量 bar 保存在[[Scopes]][0]: Script這個變量對象的屬性中,而[[Scopes]][1]: Global就是我們常說的全局對象。

(4)變量提升與暫存死區

var 聲明變量存在變量提升,如何理解變量提升呢?

要解釋清楚這個,就要涉及到執行上下文和變量對象。

在 JavaScript 代碼運行時,解釋執行全局代碼、調用函數或使用 eval 函數執行一個字符串表達式都會創建並進入一個新的執行環境,而這個執行環境被稱之為執行上下文。因此執行上下文有三類:全局執行上下文、函數執行上下文、eval 函數執行上下文。

執行上下文可以理解為一個抽象的對象,如下圖:

Variable object:變量對象,用於存儲被定義在執行上下文中的變量 (variables) 和函數聲明 (function declarations) 。

Scope chain:作用域鏈,是一個對象列表 (list of objects) ,用以檢索上下文代碼中出現的標識符 (identifiers) 。

thisValue:this 指針,是一個與執行上下文相關的特殊對象,也被稱之為上下文對象。

一個執行上下文的生命周期可以分為三個階段:創建、執行、釋放。如下圖:

南投搬家公司費用,距離,噸數怎麼算?達人教你簡易估價知識!

搬家費用:依消費者運送距離、搬運樓層、有無電梯、步行距離、特殊地形、超重物品等計價因素後,評估每車次單

而所有使用 var 聲明的變量都會在執行上下文的創建階段時作為變量對象的屬性被創建並初始化,這樣才能保證在執行階段能通過標識符在變量對象里找到對應變量進行賦值操作等。

而用 var 聲明的變量構建變量對象時進行的操作如下:

  • 由名稱和對應值(undefined)組成一個變量對象的屬性被創建(創建並初始化)
  • 如果變量名稱跟已經聲明的形式參數或函數相同,則變量聲明不會幹擾已經存在的這類屬性。

上述過程就是我們所謂的“變量提升”,這也就能解釋為什麼變量可以在聲明之前使用,因為使用是在執行階段,而在此之前的創建階段就已經將聲明的變量添加到了變量對象中,所以執行階段通過標識符可以在變量對象中查找到,也就不會報錯。

示例代碼:

console.log(a) // undefined

var a = 1;

console.log(a) // 1

let 聲明變量存在暫存死區,如何理解暫存死區呢?

其實 let 也存在與 var 類似的“變量提升”過程,但與 var 不同的是其在執行上下文的創建階段,只會創建變量而不會被初始化(undefined),並且 ES6 規定了其初始化過程是在執行上下文的執行階段(即直到它們的定義被執行時才初始化),使用未被初始化的變量將會報錯。

let and const declarations define variables that are scoped to the running execution context’s LexicalEnvironment. The variables are created when their containing Lexical Environment is instantiated but may not be accessed in any way until the variable’s LexicalBinding is evaluated. A variable defined by a LexicalBinding with an Initializer is assigned the value of its Initializer’s AssignmentExpression when the LexicalBinding is evaluated, not when the variable is created. If a LexicalBinding in a let declaration does not have an Initializer the variable is assigned the value undefined when the LexicalBinding is evaluated.

在變量初始化前訪問該變量會導致 ReferenceError,因此從進入作用域創建變量,到變量開始可被訪問的一段時間(過程),就稱為暫存死區(Temporal Dead Zone)。

示例代碼 1:

console.log(bar); // undefined
console.log(foo); // ReferenceError: foo is not defined

var bar = 1;
let foo = 2;

示例代碼 2:

var foo = 33;
{
  let foo = (foo + 55); // ReferenceError: foo is not defined
}

注:首先,需要分清變量的創建、初始化、賦值是三個不同的過程。另外,從 ES5 開始用詞法環境(Lexical Environment)替代了 ES3 中的變量對象(Variable object)來管理靜態作用域,但作用是相同的。為了方便理解,上述講解中仍保留使用變量對象來進行描述。

小結

  1. var 聲明的變量在執行上下文創建階段就會被「創建」和「初始化」,因此對於執行階段來說,可以在聲明之前使用。

  2. let 聲明的變量在執行上下文創建階段只會被「創建」而不會被「初始化」,因此對於執行階段來說,如果在其定義執行前使用,相當於使用了未被初始化的變量,會報錯。

3.let 與 const 異同

const 與 let 很類似,都具有上面提到的 let 的特性,唯一區別就在於 const 聲明的是一個只讀變量,聲明之後不允許改變其值。因此,const 一旦聲明必須初始化,否則會報錯。

示例代碼:

let a;
const b = "constant"

a = "variable"
b = 'change' // TypeError: Assignment to constant variable

如何理解聲明之後不允許改變其值?

其實 const 其實保證的不是變量的值不變,而是保證變量指向的內存地址所保存的數據不允許改動(即棧內存在的值和地址)。

JavaScript 的數據類型分為兩類:原始值類型和對象(Object類型)。

對於原始值類型(undefined、null、true/false、number、string),值就保存在變量指向的那個內存地址(在棧中),因此 const 聲明的原始值類型變量等同於常量。

對於對象類型(object,array,function等),變量指向的內存地址其實是保存了一個指向實際數據的指針,所以 const 只能保證指針是不可修改的,至於指針指向的數據結構是無法保證其不能被修改的(在堆中)。

示例代碼:

const obj = {
  value: 1
}

obj.value = 2

console.log(obj) // { value: 2 }

obj = {} // TypeError: Assignment to constant variable

4.參考

var – JavaScript | MDN

let – JavaScript – MDN – Mozilla

const – JavaScript – MDN – Mozilla

深入理解JavaScript系列(12):變量對象(Variable Object)

ES6 let 與 const

詳解ES6暫存死區TDZ

嗨,你知道 let 和 const 嗎?

本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理

※想知道購買電動車哪裡補助最多?台中電動車補助資訊懶人包彙整

節能減碳愛地球是景泰電動車的理念,是創立景泰電動車行的初衷,滿意態度更是服務客戶的最高品質,我們的成長來自於你的推薦。