20190301 es6學(xué)習(xí)—let和const命令

let命令

let與var之間的不同

聲明變量的作用域

作用:聲明變量 類似var
與var的區(qū)別:var聲明的變量將作為全局變量 而let命令聲明的變量只在let命令所在代碼塊內(nèi)有效(局部變量)
例子:

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

var聲明的i在全局范圍內(nèi)都有效,所以全局只有一個(gè)變量i,i在for循環(huán)內(nèi)不斷變化,而a[6]則是for循環(huán)過(guò)程中聲明過(guò)的函數(shù),函數(shù)內(nèi)部的i指向全局的i

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

let聲明的i只在本輪循環(huán)有效,每一次循環(huán)的i都是一個(gè)新的變量
注意: JavaScript 引擎內(nèi)部會(huì)記住上一輪循環(huán)的值,初始化本輪的變量i時(shí),就在上一輪循環(huán)的基礎(chǔ)上進(jìn)行計(jì)算

變量提升現(xiàn)象

var命令存在變量提升現(xiàn)象:
變量可以在聲明之前使用,值為undefined

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

但按照一般的邏輯,變量應(yīng)該在生命語(yǔ)句之后才可以使用,因此為了糾正這種現(xiàn)象,出現(xiàn)let命令
let命令聲明的變量一定要在聲明后使用,否則報(bào)錯(cuò),<strong>不存在變量提升現(xiàn)象<strong>

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

for循環(huán)的特別之處

設(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

循環(huán)變量i和函數(shù)內(nèi)部的變量i有各自單獨(dú)的作用域

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

只要塊級(jí)作用域內(nèi)存在let命令,它所聲明的變量就“綁定”(binding)這個(gè)區(qū)域,不再受外部的影響,即在當(dāng)前作用域內(nèi),不會(huì)受到其他作用域同名變量的影響
原因:let命令聲明的變量一定要在聲明后使用
結(jié)果:在代碼塊內(nèi),使用let命令聲明變量之前,該變量都是不可用的。這在語(yǔ)法上被稱為<strong>“暫時(shí)性死區(qū)"<strong>

var tmp = 123;

if (true) {
  tmp = 'abc'; // ReferenceError
  let tmp;
}

typeof在暫時(shí)性死區(qū)中會(huì)報(bào)錯(cuò)

//在暫時(shí)性死區(qū)中報(bào)錯(cuò)
typeof x; // ReferenceError
let x;
//變量未被聲明
typeof undeclared_variable // "undefined"
本質(zhì)

暫時(shí)性死區(qū)的本質(zhì)就是,只要一進(jìn)入當(dāng)前作用域,所要使用的變量就已經(jīng)存在了,但是不可獲取,只有等到聲明變量的那一行代碼出現(xiàn),才可以獲取和使用該變量。

不允許重復(fù)聲明

let不允許在相同作用域內(nèi),重復(fù)聲明同一個(gè)變量,因此不能在函數(shù)內(nèi)部重新聲明參數(shù)

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

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

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

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

塊級(jí)作用域

重要性

ES5只有全局作用域和函數(shù)作用域,沒(méi)有塊級(jí)作用域,這帶來(lái)很多不合理的場(chǎng)景:

  1. 內(nèi)層變量可能會(huì)覆蓋外層變量
  2. 用來(lái)計(jì)數(shù)的循環(huán)遍歷泄露為全局變量

ES6的塊級(jí)作用域

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

function f1() {
  let n = 5;
  if (true) {
    let n = 10;
  }
  console.log(n); // 5
  //若使用var聲明,則最后結(jié)果為10
}

表示外層代碼塊不受內(nèi)層代碼塊的影響(let聲明的變量只在自己的作用域內(nèi)生效)
ES6運(yùn)行塊級(jí)作用域的任意嵌套,但內(nèi)外層作用域無(wú)法讀取互相讀取變量,可以定義同名變量

{{{{
  let insane = 'Hello World';
  {let insane = 'Hello World'}
}}}};

塊級(jí)作用域與函數(shù)聲明

ES5規(guī)定,函數(shù)只能在頂層作用域和函數(shù)作用域之中聲明,不能在塊級(jí)作用域聲明
ES6引入了塊級(jí)作用域,并明確允許在塊級(jí)作用域中聲明函數(shù)
ES6 規(guī)定,塊級(jí)作用域之中,函數(shù)聲明語(yǔ)句的行為類似于let,在塊級(jí)作用域之外不可引用
ES6 的塊級(jí)作用域允許聲明函數(shù)的規(guī)則,只在使用大括號(hào)的情況下成立,如果沒(méi)有使用大括號(hào),就會(huì)報(bào)錯(cuò)

// 不報(bào)錯(cuò)
'use strict';
if (true) {
  function f() {}
}

// 報(bào)錯(cuò)
'use strict';
if (true)
  function f() {}

const命令

基本用法

const聲明一個(gè)只讀的常量,一旦聲明,就必須立即初始化,常量的值就不能改變,改變則報(bào)錯(cuò),不初始化也會(huì)報(bào)錯(cuò)

const PI = 3.1415;
PI // 3.1415

PI = 3;
// TypeError: Assignment to constant variable.

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

const作用域與let相同:只在聲明所在的塊級(jí)作用域內(nèi)有效,且聲明的變量不提升,同樣存在暫時(shí)性死區(qū),只能在聲明的位置后面使用,不可重復(fù)聲明

本質(zhì)

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

  1. 簡(jiǎn)單類型的數(shù)據(jù) 值必須保存在變量所指向的那個(gè)內(nèi)存地址,因此等同于常量
  2. 復(fù)合類型的數(shù)據(jù)(對(duì)象、數(shù)組等) 變量指向的內(nèi)存地址保存的只是一個(gè)指向?qū)嶋H數(shù)據(jù)的指針,const只能保證這個(gè)指針是固定的,至于它指向的數(shù)據(jù)結(jié)構(gòu)是不是可變的就完全不能控制了
const foo = {};

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

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

不可變的為地址,對(duì)象本身是可變的,依然可以為其添加新屬性

const a = [];
a.push('Hello'); // 可執(zhí)行
a.length = 0;    // 可執(zhí)行
a = ['Dave'];    // 報(bào)錯(cuò)

數(shù)組本身是可寫(xiě)的,但不能將另一個(gè)數(shù)組賦值給const聲明的數(shù)組變量,會(huì)報(bào)錯(cuò)

凍結(jié)對(duì)象的方法

object.freeze

const foo = Object.freeze({});

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

常量foo指向一個(gè)凍結(jié)的對(duì)象,所以添加新屬性不起作用

ES6聲明變量的方法

ES6聲明變量總共有6種方法:var,function,let,const,import,class

頂層對(duì)象的屬性

頂層對(duì)象,在瀏覽器環(huán)境指的是window對(duì)象,在Node指的是global對(duì)象
ES5之中,頂層對(duì)象的屬性和全局變量是等價(jià)的
頂層對(duì)象的屬性與全局變量掛鉤帶來(lái)的問(wèn)題:

  1. 沒(méi)法在編譯時(shí)就報(bào)出變量未聲明的錯(cuò)誤,只有運(yùn)行時(shí)才能知道(因?yàn)槿肿兞靠赡苁琼攲訉?duì)象的屬性創(chuàng)造的,而屬性的創(chuàng)造是動(dòng)態(tài)的)
  2. 程序員很容易不知不覺(jué)地就創(chuàng)建了全局變量(比如打字出錯(cuò))
  3. 頂層對(duì)象的屬性是到處可以讀寫(xiě)的,這非常不利于模塊化編程
  4. window對(duì)象有實(shí)體含義,指的是瀏覽器的窗口對(duì)象,頂層對(duì)象是一個(gè)有實(shí)體含義的對(duì)象,也是不合適的
    ES6為了改變這一點(diǎn)并保持兼容性,規(guī)定——var和function命令聲明的全局變量,依舊是頂層對(duì)象的屬性;let、const、class命令聲明的全局變量,不屬于頂層對(duì)象的屬性
var a = 1;
// 如果在 Node 的 REPL 環(huán)境,可以寫(xiě)成 global.a
// 或者采用通用方法,寫(xiě)成 this.a
window.a // 1

let b = 1;
window.b // undefined

global對(duì)象

ES5 的頂層對(duì)象由于它在各種實(shí)現(xiàn)里面是不統(tǒng)一的,所以也導(dǎo)致了很多問(wèn)題:

  1. 瀏覽器里面,頂層對(duì)象是window,但 Node 和 Web Worker 沒(méi)有window
  2. 瀏覽器和 Web Worker 里面,self也指向頂層對(duì)象,但是 Node 沒(méi)有self
  3. Node 里面,頂層對(duì)象是global,但其他環(huán)境都不支持

同一段代碼為了能夠在各種環(huán)境,都能取到頂層對(duì)象,現(xiàn)在一般是使用this變量,但是有局限性:

a. 全局環(huán)境中,this會(huì)返回頂層對(duì)象。但是,Node 模塊和 ES6 模塊中,this返回的是當(dāng)前模塊
b. 函數(shù)里面的this,如果函數(shù)不是作為對(duì)象的方法運(yùn)行,而是單純作為函數(shù)運(yùn)行,this會(huì)指向頂層對(duì)象。但是,嚴(yán)格模式下,這時(shí)this會(huì)返回undefined
c. 不管是嚴(yán)格模式,還是普通模式,new Function('return this')(),總是會(huì)返回全局對(duì)象。但是,如果瀏覽器用了 CSP(Content Security Policy,內(nèi)容安全策略),那么eval、new Function這些方法都可能無(wú)法使用

?著作權(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),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

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

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