ES6+ let 和 const 命令

知識梳理

1活箕、變量提升

變量還沒有被聲明段标,但是我們卻可以使用這個(gè)未被聲明的變量涯冠,這種情況就叫做提升,并且提升的是聲明逼庞。不僅變量可以被提升蛇更,函數(shù)也可以被提升,并且函數(shù)的提升要優(yōu)于變量的提升赛糟,函數(shù)提升會把整個(gè)函數(shù)挪到作用域頂部派任。

foo()
var foo = 5
function foo() {
  console.log('3')
}
// 輸出3

foo() // 輸出4
function foo() {
  console.log('3')
}
function foo() {
  console.log('4')
}


getName() // 輸出 1
function getName() {
  console.log('1')
}
var getName = function() {
  console.log('2')
}
getName() // 輸出2

var 命令會發(fā)生變量提升的的現(xiàn)象,即變量可在聲明之前使用璧南,而一般邏輯是先聲明變量再去使用變量掌逛。故為了糾正此現(xiàn)象,let 命令改變了語法行為司倚,即它所聲明的變量一定要在聲明后使用豆混。

// var
console.log(a);  // 輸出undefined,沒有值但不會報(bào)錯
var a = 1;

// let
console.log(b);  // 引用錯誤ReferenceError: b is not defined.
let b = 2;

2动知、作用域

在 ES5 中皿伺,只有全局作用域和函數(shù)作用域,沒有塊級作用域盒粮,這帶來了很多不合理的場景

場景一鸵鸥、內(nèi)層變量可能覆蓋外層變量:

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

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

var s = 'hello';

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

console.log(i); // 5

2.1 全局作用域

全局作用域顧名思義丹皱,就是在任何地方都能訪問到它妒穴,在瀏覽器中能通過 window 對象拿到的變量就是全局作用域下聲明的變量。

var name = 'test';
console.log(window.name)   // 輸出test
// 這里的 name 就是全局作用域下的變量

2.2 函數(shù)作用域 / 局部作用域

函數(shù)作用域就是在函數(shù)內(nèi)部定義的變量种呐,也稱局部作用域宰翅,在函數(shù)的外部不能使用這個(gè)變量

function bar() {
  var name = 'test';
}
console.log(name);  // undefined

2.3 塊級作用域

塊級作用域是 ES6 的概念,它的產(chǎn)生需要有一定的條件的爽室,在大括號 {} 中使用 let 或 const 聲明的變量汁讼,才會產(chǎn)生塊級作用域淆攻。塊級作用域的產(chǎn)生是 let 或 const 帶來的,而不是大括號嘿架,大括號的作用是限制 let 或 const 的作用域范圍瓶珊。當(dāng)不在大括號中聲明時(shí), let 或 const 的作用域范圍是全局耸彪。

if (true) let x = 1;     // 報(bào)錯
if (true) { let x = 1;}  // 不報(bào)錯

// let 方式聲明的變量在 window 下是取不到的
let a = "test";
console.log(window.a)   // undefined

// var 聲明的情況下伞芹,外層的 num 會被 {} 中的 num 覆蓋,所以沒有塊級作用域的概念
var b = 10;
{
  var b = 20;
  console.log(b)  // 20
}
console.log(b)    // 20

// let 方式聲明蝉娜,{} 內(nèi)外是互不干涉和影響的
let c = 10;
{
  // c 處于暫時(shí)性死區(qū)唱较,是不能被使用的,會報(bào)錯
  console.log(c); // Uncaught ReferenceError: Cannot access 'c' before initialization
  let c = 20;
  console.log(c)  // 20
}
console.log(c)    // 10

塊級作用域可以任意嵌套召川,每一層都是一個(gè)單獨(dú)的作用域南缓,內(nèi)層作用域可以讀取外層的變量,外層無法讀取內(nèi)層的變量會報(bào)錯

{{
  let x = 'Hello HaHa'
  {
    console.log(x); // Hello HaHa
    let y = 'Hello World'
  }
  console.log(y);   // 引用錯誤 ReferenceError: y is not defined.
}};

3荧呐、暫時(shí)性死區(qū)

暫時(shí)性死區(qū)主要是針對 let 和 const 而言的汉形,因?yàn)樗鼈儾淮嬖谧兞刻嵘栽谒鼈兟暶髯兞恐笆遣荒苁褂玫谋恫@個(gè)時(shí)候如果使用了就會報(bào)錯概疆,這時(shí)候就形成了暫時(shí)性的死區(qū),也就是不能被引用峰搪。這在語法上岔冀,稱為 “暫時(shí)性死區(qū)”(temporal dead zone,簡稱 TDZ)

{
  console.log(name);  // ReferenceError
  let name = "test";
  
  // 暫時(shí)性死區(qū) typeof 也會報(bào)錯
  typeof x; // ReferenceError
  let x;
  
  //如果一個(gè)變量根本沒有被聲明罢艾,使用typeof反而不會報(bào)錯楣颠。
  typeof undeclared_variable // 輸出 "undefined"
}

有些“死區(qū)”比較隱蔽,不太容易發(fā)現(xiàn)咐蚯。比如:

// 報(bào)錯
function bar(x = y, y = 2) {
  return [x, y];
}
bar(); // Uncaught ReferenceError: Cannot access 'y' before initialization

// 不報(bào)錯
function bar(x = 2, y = x) {
  return [x, y];
}
bar(); //[2, 2]


var x = x; // 不報(bào)錯
let x = x; // 報(bào)錯 ReferenceError: y is not defined

4童漩、let命令

let 允許你聲明一個(gè)作用域被限制在塊級中的變量、語句或者表達(dá)式春锋。var 聲明的變量只能在全局或者整個(gè)函數(shù)塊中矫膨。 var 和 let 的不同之處在于 let 是在編譯時(shí)才初始化。

let 不會在全局聲明時(shí)創(chuàng)建 window 對象的屬性期奔,但是 var 會侧馅。

4.1 不能變量提升

// var
{
  console.log(bar); // 輸出undefined,沒有值但不會報(bào)錯
  var bar = 1;
}
// let
{
  console.log(name); // 引用錯誤 ReferenceError: name is not defined.
  let name = 'test';
}

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

在代碼塊內(nèi)呐萌,使用 let 命令聲明變量之前馁痴,該變量都是不可用的

{
  console.log(name); // ReferenceError
  let name = 'test';
}

4.2 重復(fù)聲明報(bào)錯

let 不允許在同一個(gè)函數(shù)或塊作用域中重復(fù)聲明同一個(gè)變量,否則會引起語法錯誤(SyntaxError)肺孤,即使用 var 去聲明也是不可以的

{
  let x = 10  // 10
  let x = 18  // Uncaught SyntaxError: Identifier 'x' has already been declared 
}

{
  let y = 10; // 10
  var y = 1;  //Uncaught SyntaxError: Identifier 'y' has already been declared 
}

注意在 switch 語句中只有一個(gè)塊級作用域罗晕,所以下面這種情況也是會報(bào)錯的

// 報(bào)錯
let x = 1;
switch(x) {
  case 0:
    let num;
    break;
  case 1:
    let num; //重復(fù)聲明了
    break;
}

// 不報(bào)錯
let x = 1;
switch(x) {
  case 0: {// 塊
    let num;
    break;
  } 
  case 1: {// 塊
    let num;// 這里可以正常聲明
    break;
  }
}

5济欢、const 命令

const 的使用類似于 let:

  • let 和 const 都只作用于塊級作用域內(nèi);
  • 不能進(jìn)行變量提升小渊,在聲明前不能被使用法褥,否則會拋出異常;
  • 存在暫時(shí)性死區(qū)酬屉,在塊中不能被重復(fù)聲明半等,重復(fù)用var聲明也不行;

不同的是 const 在聲明時(shí)必須初始化一個(gè)值呐萨,而且這個(gè)值不能被改變杀饵。

// 在聲明時(shí)必須初始化一個(gè)值
const PI; // Uncaught SyntaxError: Missing initializer in const declaration

// 不能被修改
const PI = 3.1415;  // 定義一個(gè)圓周率常量 PI
PI = 12  // Uncaught TypeError: Assignment to constant variable.

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

對于簡單類型的數(shù)據(jù)(數(shù)值、字符串怯屉、布爾值),值就保存在變量指向的那個(gè)內(nèi)存地址饵沧,因此等同于常量锨络。

但對于復(fù)合類型的數(shù)據(jù)(主要是對象和數(shù)組),變量指向的內(nèi)存地址狼牺,保存的只是一個(gè)指向?qū)嶋H數(shù)據(jù)的指針羡儿,const只能保證這個(gè)指針是固定的(即總是指向另一個(gè)固定的地址)

const obj = {};
obj.a = 12     // 12
const arr = [];
arr.push(12);  // 12
arr = {};      // Uncaught TypeError: Assignment to constant variable.

6、var是钥、let 及 const 區(qū)別

聲明方式 var let const
作用域 非塊級 塊級 塊級
變量提升 允許 不允許 不允許
暫時(shí)性死區(qū) 不存在 存在 存在
重復(fù)聲明 允許 不允許 不允許
初始值 不需要 不需要 需要

代碼練習(xí)

Q1:下面的代碼輸出什么?

function sayHi() {
  console.log(name) 
  console.log(age)
  var name = 'Lydia'
  let age = 21
}

sayHi(); // 輸出 undefined 和 ReferenceError

Q2:下面的代碼輸出什么?

for (var i = 0; i < 3; i++) {
  setTimeout(() => {
    console.log(i);
  }, 1000*i);
}
//依次輸出 3 3 3

for (let i = 0; i < 3; i++) {
  setTimeout(() => {
    console.log(i);
  }, 1000 * i);
}
//依次輸出 0 1 2

Q3:下面的代碼輸出什么?

for (let i = 0; i < 3; i++) {
  let i = 'abc';
  console.log(i);
}
// 依次輸出 abc abc abc
// for 循環(huán)掠归,設(shè)置循環(huán)變量的那部分是一個(gè)父作用域,而循環(huán)體內(nèi)部是一個(gè)單獨(dú)的子作用域

Q4:下面的代碼輸出什么?

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

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

學(xué)習(xí)鏈接

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末悄泥,一起剝皮案震驚了整個(gè)濱河市虏冻,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌弹囚,老刑警劉巖厨相,帶你破解...
    沈念sama閱讀 216,496評論 6 501
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異鸥鹉,居然都是意外死亡蛮穿,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,407評論 3 392
  • 文/潘曉璐 我一進(jìn)店門毁渗,熙熙樓的掌柜王于貴愁眉苦臉地迎上來践磅,“玉大人,你說我怎么就攤上這事灸异「剩” “怎么了羔飞?”我有些...
    開封第一講書人閱讀 162,632評論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長细溅。 經(jīng)常有香客問我褥傍,道長,這世上最難降的妖魔是什么喇聊? 我笑而不...
    開封第一講書人閱讀 58,180評論 1 292
  • 正文 為了忘掉前任恍风,我火速辦了婚禮,結(jié)果婚禮上誓篱,老公的妹妹穿的比我還像新娘朋贬。我一直安慰自己,他們只是感情好窜骄,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,198評論 6 388
  • 文/花漫 我一把揭開白布锦募。 她就那樣靜靜地躺著,像睡著了一般邻遏。 火紅的嫁衣襯著肌膚如雪糠亩。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,165評論 1 299
  • 那天准验,我揣著相機(jī)與錄音赎线,去河邊找鬼。 笑死糊饱,一個(gè)胖子當(dāng)著我的面吹牛垂寥,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播另锋,決...
    沈念sama閱讀 40,052評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼滞项,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了夭坪?” 一聲冷哼從身側(cè)響起文判,我...
    開封第一講書人閱讀 38,910評論 0 274
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎室梅,沒想到半個(gè)月后律杠,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,324評論 1 310
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡竞惋,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,542評論 2 332
  • 正文 我和宋清朗相戀三年柜去,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片拆宛。...
    茶點(diǎn)故事閱讀 39,711評論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡嗓奢,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出浑厚,到底是詐尸還是另有隱情股耽,我是刑警寧澤根盒,帶...
    沈念sama閱讀 35,424評論 5 343
  • 正文 年R本政府宣布,位于F島的核電站物蝙,受9級特大地震影響炎滞,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜诬乞,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,017評論 3 326
  • 文/蒙蒙 一册赛、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧震嫉,春花似錦森瘪、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,668評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至悴势,卻和暖如春窗宇,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背特纤。 一陣腳步聲響...
    開封第一講書人閱讀 32,823評論 1 269
  • 我被黑心中介騙來泰國打工担映, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人叫潦。 一個(gè)月前我還...
    沈念sama閱讀 47,722評論 2 368
  • 正文 我出身青樓,卻偏偏與公主長得像官硝,于是被迫代替她去往敵國和親矗蕊。 傳聞我的和親對象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,611評論 2 353