前言:
最近開始看阮一峰老師的《ECMAScript 6 入門》(以下簡(jiǎn)稱原文)學(xué)習(xí)ECMAScript 6(下文簡(jiǎn)稱ES6)的知識(shí),整理出一些知識(shí)點(diǎn)加上我的理解來(lái)做成文章筆記。按照章節(jié)為單位一個(gè)章節(jié)一篇筆記。
文章代碼與目錄結(jié)構(gòu)和原文不同。
這一章原文鏈接 let 和 const 命令 。
let
let 是用來(lái)聲明一個(gè)變量。
不同與var會(huì)存在變量提升(下文有介紹),let所聲明的變量值只在let命令所在的代碼塊內(nèi)有效。
同一個(gè)作用域(下文有介紹)不可使用 let 重復(fù)聲明同一個(gè)變量。
注意:
- 聲明變量
- 沒有變量提升
- 不可重復(fù)聲明
- 只在
let命令所在代碼塊有效
let sample = 1;
sample = 2;
let sample = 2; // 將會(huì)報(bào)錯(cuò)
{
let sample = 1;
console.log(sample); // 正常輸出 1
}
console.log(sample); // 將會(huì)報(bào)錯(cuò),因?yàn)橹辉趌et命令所在代碼塊有效
const
const 是用來(lái)聲明一個(gè)只讀常量。
一旦聲明,常量的值就不能改變。如果試著改變常量的值會(huì)報(bào)錯(cuò)。
并且const 在聲明的時(shí)候就必須對(duì)其賦值,只聲明不賦值,也會(huì)報(bào)錯(cuò)。
同一個(gè)作用域不可使用 const 重復(fù)聲明同一個(gè)常量。
const 與let一樣,都因?yàn)樽饔糜蛟?,只能在所在代碼塊中有效。
const實(shí)際上保證的,并不是變量的值不得改動(dòng),而是變量指向的那個(gè)內(nèi)存地址所保存的數(shù)據(jù)不得改動(dòng)。
注意:
- 聲明常量
- 聲明后不可以改變
- 聲明的時(shí)候必須對(duì)其賦值
- 不可重復(fù)聲明
- 在
const命令所在代碼塊有效
const sample = 1;
sample = 2; // 將會(huì)報(bào)錯(cuò),const 聲明的變量不可以重新賦值
const sample; // 直接報(bào)錯(cuò),const 聲明的時(shí)候必須對(duì)其賦值
let 與 const
引入let后,已經(jīng)可以代替var了,在let與const之中能用const就盡量用const。
let 與 const 不同處
let 與 const 的區(qū)別就是一個(gè)聲明變量一個(gè)聲明常量,變量可以重新賦值,常量不能重新賦值。
let sampleLet = 2;
const sampleConst = 1;
sampleLet = 3; // 正常
sampleConst = 3; // 報(bào)錯(cuò)
let 與 const 相同處
- 都只能先聲明后使用,不能變量提升。
- 都不可以在同一個(gè)作用域中重復(fù)聲明
- 都只在命令所在代碼塊有效
{
sampleLet; // 報(bào)錯(cuò)
sampleConst; // 報(bào)錯(cuò)
let sampleLet = 2;
const sampleConst = 1;
let sampleLet = 3; // 報(bào)錯(cuò)
const sampleConst = 3; // 報(bào)錯(cuò)
}
sampleLet; // 報(bào)錯(cuò)
sampleConst; // 報(bào)錯(cuò)
變量提升(Hoisting)
在ES6之前,使用var聲明變量時(shí)會(huì)產(chǎn)生一種叫做變量提升的特性。
無(wú)論是在代碼的哪個(gè)地方聲明的,都會(huì)提升到當(dāng)前作用域的最頂部,這種行為叫做變量提升。
為了糾正這種現(xiàn)象,let 命令改變了語(yǔ)法行為,它所聲明的變量一定要在聲明后使用,否則報(bào)錯(cuò)
上文
let與const表示變量不能提升,真的是這樣嗎?
其實(shí)在 JavaScript 中,所有表示var, let, const, function, function*, class的聲明都會(huì)被提升。
let與const聲明變量會(huì)在環(huán)境實(shí)例化時(shí)被創(chuàng)建,但是在變量的詞法綁定之前不允許以任何方式對(duì)其進(jìn)行訪問,也就是說(shuō),當(dāng)你在聲明前調(diào)用變量將會(huì)報(bào)錯(cuò)但是報(bào)錯(cuò)信息不是未定義而是無(wú)法在初始化之前訪問。這里也就引出了下一個(gè)概念,叫做暫時(shí)性死區(qū)。
// var 聲明會(huì)變量提升,不會(huì)報(bào)錯(cuò),但是值為 undefined
console.log(sampleVar); // undefined
var sampleVar = 1;
// let 聲明不會(huì)變量提升,但是報(bào)錯(cuò)不是 not defined
console.log(sampleLet); // Cannot access 'sampleLet' before initialization
let sampleLet = 1;
// const 聲明不會(huì)變量提升,但是報(bào)錯(cuò)不是 not defined
console.log(sampleConst); // Cannot access 'sampleConst' before initialization
const sampleConst = 1;
// 直接使用沒有聲明的變量報(bào)錯(cuò)為 ” is not defined “
console.log(sample); //sample is not defined
暫時(shí)性死區(qū)
ES6 規(guī)定,如果代碼區(qū)塊中存在 let 和 const 命令聲明的變量,這個(gè)區(qū)塊對(duì)這些變量從一開始就形成了封閉作用域,凡是在聲明之前就使用這些變量,就會(huì)報(bào)錯(cuò)。直到聲明語(yǔ)句完成,這些變量才能被訪問(獲取或設(shè)置),
這在語(yǔ)法上稱為“暫時(shí)性死區(qū)”(英temporal dead zone,簡(jiǎn) TDZ),即代碼塊開始到變量聲明語(yǔ)句完成之間的區(qū)域。
var sample = 1;
if (true) {
sample = '1'; // 報(bào)錯(cuò)
let sample;
}
簡(jiǎn)單來(lái)說(shuō),就是let 和 const 命令聲明的變量,在進(jìn)入這個(gè)聲明代碼所在的作用域時(shí),就已經(jīng)存在,但是不可以獲取或使用,直到聲明語(yǔ)句完成,才可以訪問。
塊級(jí)作用域
作用域(scope,或譯有效范圍)就是變量和函數(shù)的可訪問范圍,即作用域控制著變量和函數(shù)的可見性和生命周期。
let與const 塊級(jí)作用域
作用域并不是ES6的新東西,但是在ES5只有全局作用域和函數(shù)作用域,為了解決塊級(jí)作用域,ES6可以使用**let**與**const**聲明一個(gè)塊級(jí)作用域的變量。
var 聲明的變量具有變量提升特性,所以沒有塊的概念,可以跨塊訪問,但不能跨函數(shù)。
外層作用域無(wú)法讀取內(nèi)層作用域的變量。
{ // 塊作用域
var sampleVar = 1;
let sampleLet = 2;
const sampleConst = 3;
console.log(sampleVar); // 成功輸出 1
console.log(sampleLet); // 成功輸出 2
console.log(sampleConst); // 成功輸出 3
}
console.log(sampleVar); // 成功輸出 1
console.log(sampleLet); // 報(bào)錯(cuò) not defined
console.log(sampleConst); // 報(bào)錯(cuò) not defined
ES6 允許塊級(jí)作用域的任意嵌套。
同一個(gè)作用域不可使用let或const聲明同一個(gè)變量,內(nèi)層作用域可以定義外層作用域的同名變量。
{
{
{
let sample = 'Hello World'; // 外層作用域
{ let sample = 'sample'; } // 不報(bào)錯(cuò)
{ console.log(sample); } // 正常輸出 ‘Hello World’
}
}
}
塊級(jí)作用域與函數(shù)聲明
ES5 規(guī)定,函數(shù)只能在頂層作用域和函數(shù)作用域之中聲明,不能在塊級(jí)作用域聲明。
ES6 規(guī)定,塊級(jí)作用域之中,函數(shù)聲明語(yǔ)句的行為類似于**let**,在塊級(jí)作用域之外不可引用。
/*
ES5,這兩種情況都是不合法的,因?yàn)檫@兩個(gè)函數(shù)聲明都是在塊作用域中聲明。
但應(yīng)為瀏覽器為了兼容以前的舊代碼,還是支持在塊級(jí)作用域之中聲明函數(shù)。所以不會(huì)報(bào)錯(cuò)
*/
if (true) {
function sampleFn() {}
}
try {
function sampleFn() {}
} catch(e) {
// ...
}
/*
ES6,函數(shù)聲明語(yǔ)句的行為類似于let,在塊級(jí)作用域之外不可引用
*/
if (true) {
sampleFn(); // 正常輸出,函數(shù)聲明語(yǔ)句的行為類似于let
function sampleFn() {
console.log('Hello World');
}
}
// 但其實(shí)在塊級(jí)作用域之外也可以引用函數(shù),只不過(guò)值為undefined
if (false) {
function sampleFn() { console.log('Hello World'); }
}
console.log(sampleFn); // 正常輸出 undefined
sampleFn(); // 報(bào)錯(cuò)為sampleFdddn is not defined
為什么塊級(jí)作用域之外也可以引用函數(shù)呢?
如果改變了塊級(jí)作用域內(nèi)聲明的函數(shù)的處理規(guī)則,顯然會(huì)對(duì)老代碼產(chǎn)生很大影響。為了減輕因此產(chǎn)生的不兼容問題,ES6 在附錄 B里面規(guī)定,瀏覽器的實(shí)現(xiàn)可以不遵守上面的規(guī)定(指函數(shù)聲明語(yǔ)句的行為),有自己的行為方式。
- 允許在塊級(jí)作用域內(nèi)聲明函數(shù)。
-
函數(shù)聲明類似于
**var**,即會(huì)提升到全局作用域或函數(shù)作用域的頭部。 - 同時(shí),函數(shù)聲明還會(huì)提升到所在的塊級(jí)作用域的頭部。
注意,上面三條規(guī)則只對(duì) ES6 的瀏覽器實(shí)現(xiàn)有效,其他環(huán)境的實(shí)現(xiàn)不用遵守,還是將塊級(jí)作用域的函數(shù)聲明當(dāng)作let處理。
我們應(yīng)該避免在塊級(jí)作用域內(nèi)聲明函數(shù)。如果確實(shí)需要,也應(yīng)該寫成函數(shù)表達(dá)式,而不是函數(shù)聲明語(yǔ)句。
// 函數(shù)聲明語(yǔ)句,不要在塊作用域中使用,因?yàn)闀?huì)有變量提升
{
function sampleFn() {
console.log("Hello World");
}
}
// 函數(shù)表達(dá)式,在塊作用域中,函數(shù)不會(huì)有變量提升
{
const sampleFn = function () {
console.log("Hello World");
}
}
頂層對(duì)象
頂層對(duì)象,在瀏覽器環(huán)境指的是window對(duì)象。
ES5 之中,頂層對(duì)象的屬性與全局變量是等價(jià)的。
ES6 為了改變這一點(diǎn),
一方面規(guī)定,為了保持兼容性,var命令和function命令聲明的全局變量,依舊是頂層對(duì)象的屬性;
另一方面規(guī)定,let命令、const命令、class命令聲明的全局變量,不屬于頂層對(duì)象的屬性。
/*
ES5 之中,頂層對(duì)象的屬性賦值與全局變量的賦值,是同一件事。
*/
window.sample = 1;
console.log(window.sample); // 正常輸出 1
sample = 2;
console.log(window.sample);// 正常輸出 2
/*
ES6 之中,let命令、const命令、class命令聲明的全局變量,不屬于頂層對(duì)象的屬性。
*/
var sampleVar = 1;
console.log(window.sampleVar) // 正常輸出 1
let sampleLet = 1;
console.log(window.sampleLet) // 正常輸出 undefined
let sampleConst = 1;
console.log(window.sampleConst) // 正常輸出 undefined
window提供全局環(huán)境(即全局作用域)所有代碼都是在這個(gè)環(huán)境中運(yùn)行。
函數(shù)里面的this,如果函數(shù)不是作為對(duì)象的方法運(yùn)行,而是單純作為函數(shù)運(yùn)行,this會(huì)指向頂層對(duì)象。但是,嚴(yán)格模式下,這時(shí)this會(huì)返回undefined。
不管是嚴(yán)格模式,還是普通模式,new Function('return this')(),總是會(huì)返回全局對(duì)象。
function sampleFn(){
console.log(this);
}
sampleFn(); // 正常輸出 輸出全局對(duì)象 window
function sampleFn1(){
"use strict";
console.log(this)
}
sampleFn1(); // 正常輸出 undefined
// 開啟嚴(yán)格模式
"use strict";
const sample = new Function('return this')();
console.log(sample); // 正常輸出 輸出全局對(duì)象 window