let和const命令

1、let命令

ES6 新增了let命令,用來聲明變量。它的用法類似于var,但是所聲明的變量,只在let命令所在的代碼塊內(nèi)(一個(gè)大括號(hào)可以稱為一個(gè)代碼塊)有效。

{
  let a = 10;
  var b = 1;
}

console.log(a) // ReferenceError: a is not defined.
console.log(b) // 1

for循環(huán)的計(jì)數(shù)器,就很合適使用let命令。

var a = [];
for (let i = 0; i < 10; i++) {
  a[i] = function () {
    console.log(i);
  };
}
a[6](); // 6

下面的代碼如果使用var,最后輸出的是10。

var a = [];
for (var i = 0; i < 10; i++) {
  a[i] = function () {
    console.log(i);
  };
}
a[6](); // 10

另外,for循環(huán)還有一個(gè)特別之處,就是設(shè)置循環(huán)變量的那部分是一個(gè)父作用域,而循環(huán)體內(nèi)部是一個(gè)單獨(dú)的子作用域。

for (let i = 0; i < 3; i++) {
  let i = 'abc';
  console.log(i);
}
// abc
// abc
// abc

上面代碼正確運(yùn)行,輸出了 3 次abc。這表明函數(shù)內(nèi)部的變量i與循環(huán)變量i不在同一個(gè)作用域,有各自單獨(dú)的作用域。

不存在變量提升

var命令會(huì)發(fā)生”變量提升“現(xiàn)象,即變量可以在聲明之前使用,值為undefined。
let命令改變了語法行為,它所聲明的變量一定要在聲明后使用,否則報(bào)錯(cuò)。

// var 的情況
console.log(foo); // 輸出undefined
var foo = 2;

// let 的情況
console.log(bar); // 報(bào)錯(cuò)ReferenceError
let bar = 2;

暫時(shí)性死區(qū)

在代碼塊內(nèi),使用let命令聲明變量之前,該變量都是不可用的。這在語法上,稱為“暫時(shí)性死區(qū)”(temporal dead zone,簡稱 TDZ)。

if (true) {
  // TDZ開始
  tmp = 'abc'; // ReferenceError
  console.log(tmp); // ReferenceError

  let tmp; // TDZ結(jié)束
  console.log(tmp); // undefined

  tmp = 123;
  console.log(tmp); // 123
}

上面代碼中,在let命令聲明變量tmp之前,都屬于變量tmp的“死區(qū)”。
作為比較,如果一個(gè)變量根本沒有被聲明,使用typeof反而不會(huì)報(bào)錯(cuò)。

typeof undeclared_variable // "undefined"

不允許重復(fù)聲明

let不允許在相同作用域內(nèi),重復(fù)聲明同一個(gè)變量。

// 報(bào)錯(cuò)
function func() {
  let a = 10;
  var a = 1;
}

// 報(bào)錯(cuò)
function func() {
  let a = 10;
  let a = 1;
}
因此,不能在函數(shù)內(nèi)部重新聲明參數(shù)。

function func(arg) {
  let arg; // 報(bào)錯(cuò)
}

function func(arg) {
  {
    let arg; // 不報(bào)錯(cuò)
  }
}

2、塊級(jí)作用域

為什么使用塊級(jí)作用域

第一種場景,內(nèi)層變量可能會(huì)覆蓋外層變量。

var tmp = new Date();

function f() {
  console.log(tmp);
  if (false) {
    var tmp = 'hello world';
  }
}

f(); // undefined

上面代碼的原意是,if代碼塊的外部使用外層的tmp變量,內(nèi)部使用內(nèi)層的tmp變量。但是,函數(shù)f執(zhí)行后,輸出結(jié)果為undefined,原因在于變量提升,導(dǎo)致內(nèi)層的tmp變量覆蓋了外層的tmp變量。

第二種場景,用來計(jì)數(shù)的循環(huán)變量泄露為全局變量。

var s = 'hello';

for (var i = 0; i < s.length; i++) {
  console.log(s[i]);
}

console.log(i); // 5

上面代碼中,變量i只用來控制循環(huán),但是循環(huán)結(jié)束后,它并沒有消失,泄露成了全局變量。

ES6塊級(jí)作用域

let實(shí)際上為 JavaScript 新增了塊級(jí)作用域。

function f1() {
  let n = 5;
  if (true) {
    let n = 10;
  }
  console.log(n); // 5
}

考慮到環(huán)境導(dǎo)致的行為差異太大,應(yīng)該避免在塊級(jí)作用域內(nèi)聲明函數(shù)。如果確實(shí)需要,也應(yīng)該寫成函數(shù)表達(dá)式,而不是函數(shù)聲明語句。

// 函數(shù)聲明語句
{
  let a = 'secret';
  function f() {
    return a;
  }
}

// 函數(shù)表達(dá)式
{
  let a = 'secret';
  let f = function () {
    return a;
  };
}

3、const命令

const聲明一個(gè)只讀的常量。一旦聲明,常量的值就不能改變。這意味著,const一旦聲明變量,就必須立即初始化,不能留到以后賦值。

const foo;
// SyntaxError: Missing initializer in const declaration

***const的作用域與let命令相同:只在聲明所在的塊級(jí)作用域內(nèi)有效。
const聲明的常量,也與let一樣不可重復(fù)聲明。

var message = "Hello!";
let age = 25;

// 以下兩行都會(huì)報(bào)錯(cuò)
const message = "Goodbye!";
const age = 30;

const實(shí)際上保證的,并不是變量的值不得改動(dòng),而是變量指向的那個(gè)內(nèi)存地址所保存的數(shù)據(jù)不得改動(dòng)。

const foo = {};

// 為 foo 添加一個(gè)屬性,可以成功
foo.prop = 123;
foo.prop // 123

// 將 foo 指向另一個(gè)對(duì)象,就會(huì)報(bào)錯(cuò)
foo = {}; // TypeError: "foo" is read-only

上面代碼中,常量foo儲(chǔ)存的是一個(gè)地址,這個(gè)地址指向一個(gè)對(duì)象。不可變的只是這個(gè)地址,即不能把foo指向另一個(gè)地址,但對(duì)象本身是可變的,所以依然可以為其添加新屬性。

如果真的想將對(duì)象凍結(jié),應(yīng)該使用Object.freeze方法。凍結(jié)之后對(duì)象的屬性不可更改

const foo = Object.freeze({});

// 常規(guī)模式時(shí),下面一行不起作用;
// 嚴(yán)格模式時(shí),該行會(huì)報(bào)錯(cuò)
foo.prop = 123;

上面代碼中,常量foo指向一個(gè)凍結(jié)的對(duì)象,所以添加新屬性不起作用,嚴(yán)格模式時(shí)還會(huì)報(bào)錯(cuò)。

ES6聲明變量的六種方法

ES5 只有兩種聲明變量的方法:var命令和function命令。ES6 除了添加let和const命令,后面章節(jié)還會(huì)提到,另外兩種聲明變量的方法:import命令和class命令。所以,ES6 一共有 6 種聲明變量的方法。

頂層對(duì)象

頂層對(duì)象,在瀏覽器環(huán)境指的是window對(duì)象,在 Node 指的是global對(duì)象。ES5 之中,頂層對(duì)象的屬性與全局變量是等價(jià)的。

window.a = 1;
a // 1

a = 2;
window.a // 2
var a = 1;
// 如果在 Node 的 REPL 環(huán)境,可以寫成 global.a
// 或者采用通用方法,寫成 this.a
window.a // 1

let b = 1;
window.b // undefined

上面代碼中,全局變量a由var命令聲明,所以它是頂層對(duì)象的屬性;全局變量b由let命令聲明,所以它不是頂層對(duì)象的屬性,返回undefined。

5、global 對(duì)象

ES5 的頂層對(duì)象,本身也是一個(gè)問題,因?yàn)樗诟鞣N實(shí)現(xiàn)里面是不統(tǒng)一的。

瀏覽器里面,頂層對(duì)象是window,但 Node 和 Web Worker 沒有window。
瀏覽器和 Web Worker 里面,self也指向頂層對(duì)象,但是 Node 沒有self。
Node 里面,頂層對(duì)象是global,但其他環(huán)境都不支持。
墊片庫system.global模擬了這個(gè)提案,可以在所有環(huán)境拿到global。

// CommonJS 的寫法
require('system.global/shim')();

// ES6 模塊的寫法
import shim from 'system.global/shim'; shim();

上面代碼可以保證各種環(huán)境里面,global對(duì)象都是存在的。

// CommonJS 的寫法
var global = require('system.global')();

// ES6 模塊的寫法
import getGlobal from 'system.global';
const global = getGlobal();

上面代碼將頂層對(duì)象放入變量global。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • let 命令 塊級(jí)作用域 const 命令 頂層對(duì)象的屬性 global 對(duì)象 let 命令 基本用法 ES6 新...
    嘉奇呦_nice閱讀 1,695評(píng)論 0 2
  • let 命令 塊級(jí)作用域 const 命令 頂層對(duì)象的屬性 global 對(duì)象 let 命令 基本用法 ES6 新...
    卞卞村長L閱讀 683評(píng)論 0 0
  • let 和 const 命令 let 命令 塊級(jí)作用域 const 命令 頂層對(duì)象的屬性 gl...
    安小明閱讀 1,043評(píng)論 0 0
  • let 命令 1.基本用法 let 是ES6新增的命令,用于聲明變量。用法類似于var,但是聲明的變量只在let所...
    站在大神的肩膀上看世界閱讀 394評(píng)論 0 3
  • let命令 基本用法 ES6新增了let命令,用來聲明變量。它的用法類似于var,但是所聲明的變量,只在let命令...
    陳老板_閱讀 322評(píng)論 0 0

友情鏈接更多精彩內(nèi)容