面試官:說說 let 和 const 吧

let 和 const 是 ES6 新增的用於聲明變量和常量的關鍵字,他們的做用是什麼?它們又有什麼特性?它們與 var 定義的變量又有何區別?javascript

let 和 const

定義之後可改變的量就是變量,定義後不可改變的量就是常量。前端

1. 變量和常量

ES6 中,使用 let 命令定義變量,使用 const 命令定義常量,也就是說 let 定義後變量是可修改的,const 定義後的常量不能被修改。這一特性目前被大多數瀏覽器原生支持,可是針對少部分不能支持的瀏覽器,咱們可使用 babel 將它編譯成 ES5 語法,下面能夠看下二者用 babel 編譯後的代碼有何區別:java

// ES6
let a = 1;
const b = 2;
複製代碼
// babel 編譯後
"use strict";

var a = 1;
var b = 2;
複製代碼

感受沒區別啊??不要急,再加一段代碼:webpack

// ES6
let a = 1;
const b = 2;
b = 3;
複製代碼
// babel 編譯後
"use strict";

function _readOnlyError(name) { throw new Error("\"" + name + "\" is read-only"); }

var a = 1;
var b = 2;
b = (_readOnlyError("b"), 3);
複製代碼

這樣應該很容易看出區別了,當咱們對一個常量進行變值操做,就會拋出一個錯誤告訴你:這個值是隻讀的。git

b = (_readOnlyError("b"), 3) 這種操做我也沒見過,不過猜想應該相似於 b = _readOnlyError("b") && 3es6

注意:因爲const 定義的值是不可變的,這點在用 const 定義引用類型的時候要特別注意!若是是引用類型的 const 值,改變其中的屬性是可行的,可是一般不建議這麼作。web

const obj = {};
obj.a = 1;
obj.b = 2;
obj; // => {a: 1, b: 2}

obj = {}; // => 報錯
複製代碼

2. 定義就要初始化

因爲 const 定義的常量,定義後就不能修改的特性,決定了它定義的時候必須就初始化,不然就報錯;而 let 則沒有這種限制,它定義的變量徹底能夠在後面再初始化:面試

let a;
a = 1;

const b; // Uncaught SyntaxError: Missing initializer in const declaration
b = 2;
複製代碼

上面這段代碼因爲直接違反了 const 的語法特性,所以在 babel 編譯階段就沒法經過express

let 和 var

letvar 是兩種聲明變量的方式,兩者主要有如下區別:瀏覽器

1. 做用域不一樣

  • let 所聲明的變量會建立本身的塊級做用域,建立的做用域是定義它的塊級代碼及其中包括的子塊中,且沒法自動往全局變量 window 上綁定屬性。
  • var 定義的變量,做用域爲定義它的函數,或者全局,而且是能自動往全局對象 window 上綁定屬性的。

關於可否建立本身的塊級做用域這一差異,就會涉及到一道被問爛的面試題:

var result = [];
(function () {
  for (var i = 0; i < 5; i++) {
    result.push(function () {
      console.log(i);
    });
  }
})();
result.forEach(function (item) {
  item()
});
// => 打印出五個 5
複製代碼

爲何打印出了五個5,而不是預期的 0,1,2,3,4 ?由於 var 不會建立本身的做用域,而 js 自己又是沒有塊級做用域這個概念的,for 循環中定義的變量就等因而直接定義在匿名函數中的變量,因而當這5個函數被掏出來執行的時候,循環早已完成,而函數讀取的上面一層做用域中存儲的變量 i,也早已經被累加成了5。

然而,這個問題只要將 var 關鍵字換成 let 就迎刃而解:

var result = [];
(function () {
  for (let i = 0; i < 5; i++) {
    result.push(function () {
      console.log(i);
    });
  }
})();
result.forEach(function (item) {
  item()
}); // => 0,1,2,3,4
複製代碼

咱們能夠看看這段使用 let 的代碼,最後被 babel 轉譯成什麼樣:

"use strict";

var result = [];

(function () {
  var _loop = function _loop(i) {
    result.push(function () {
      console.log(i);
    });
  };

  for (var i = 0; i < 5; i++) {
    _loop(i);
  }
})();

result.forEach(function (item) {
  item();
});
複製代碼

從上面的代碼咱們就能夠看出,let 建立做用域的方式,其實就是建立了一個函數,在函數內定義一個同名變量並於外部將這個變量傳入其中,以此達到建立做用域的目的。

2. 變量聲明提高

  • let 定義的變量不會進行變量聲明提高操做,也就是說在訪問該變量以前必需要先定義。
  • var 定義的變量存在變量聲明提高,所以在變量定義前就能訪問到變量,值是 undefined
console.log(a); // undefined
var a = 1;

console.log(b); // Uncaught ReferenceError: Cannot access 'b' before initialization
let b = 2;
複製代碼

一樣咱們來看下上面這段代碼被編譯後的樣子:

"use strict";

console.log(a);
var a = 1;

console.log(b);
var b = 2;
複製代碼

看起來好像 Babel 沒法編譯這種阻止變量聲明提高的語法,let 聲明的變量沒法提高的特性應該是瀏覽器內部的 JS 執行引擎支持和實現的。

3. 暫時性死區

只要塊級做用域內存在let / const命令,它所聲明的變量 / 常量就「綁定」(binding)這個區域,再也不受外部的影響。ES6 明確規定,若是區塊中存在letconst命令,這個區塊對這些命令聲明的變量,從一開始就造成了封閉做用域,凡是在聲明以前就使用這些變量,就會報錯。這種特性也被成爲暫時性死區。

var tmp = 123;

if (true) {
  tmp = 'abc'; // ReferenceError: tmp is not defined
  let tmp;
}
複製代碼

一樣,這個特性也是被瀏覽器內部的 JS 執行引擎支持和實現的,babel 沒法支持這種特性的編譯,只能簡單的將 let 編譯成 var。可是有意思的是,因爲 letif 塊中是能夠構建本身的獨立做用域的,babeltmp 這個變量換了個名字來模擬實現塊級做用域的建立:

"use strict";

var tmp = 123;

if (true) {
  _tmp = 'abc';

  var _tmp;
}
複製代碼

4. 能否重複定義

  • let 定義的變量,一旦定義便不容許被從新定義。
  • var 定義的變量,能夠被從新定義。
var a = 1;
var a = 2;

let b = 3;
let b = 4; // Uncaught SyntaxError: Identifier 'b' has already been declared
複製代碼

上面這段代碼因爲直接違反了 let 定義的變量沒法從新定義的語法特性,所以一樣在 babel 編譯階段就沒法經過。

總結

這塊內容並不複雜,咱們能夠作一個簡單的總結:

let 特性:

  • 建立塊級做用域。

  • 定義後不能從新定義。

  • 不存在變量提高。

  • 存在暫時性死區。

  • 全局做用域下定義時不會被掛載到頂層對象上(window對象 / global 對象

    // 瀏覽器環境
    var a = 1;
    window.a; // => 1
    
    let b = 2;
    window.b; // => undefined
    複製代碼

const 特性:

  • let
  • 一旦初始化賦值,後面不能被修改
  • 定義時就必須初始化

本篇文章已收錄入 前端面試指南專欄

相關參考

往期內容推薦

  1. 完全弄懂節流和防抖
  2. 【基礎】HTTP、TCP/IP 協議的原理及應用
  3. 【實戰】webpack4 + ejs + express 帶你擼一個多頁應用項目架構
  4. 瀏覽器下的 Event Loop
  5. 面試官:說說執行上下文吧
  6. 面試官:說說做用域和閉包吧
  7. 面試官:說說原型鏈和繼承吧
  8. 面試官:說說 JS 中的模塊化吧
相關文章
相關標籤/搜索