let 和 const

let 和const是es6新增的命令亡驰,用于聲明變量

var和let/const的區(qū)別
1.塊級(jí)作用域
2.不存在變量提升
3.暫時(shí)性死區(qū)
4.不可重復(fù)聲明
5.let const聲明的全局變量不會(huì)掛在頂層對(duì)象下面

const 需注意倆點(diǎn):

  1. const 聲明之后必須馬上賦值赡艰,否則會(huì)報(bào)錯(cuò)
    2.const簡(jiǎn)單類型一旦聲明就不能再更改宠叼,復(fù)雜類型(數(shù)組舍沙,對(duì)象等)指針指向的地址不能更改宗兼,但是內(nèi)部數(shù)據(jù)可以更改咆耿。
塊級(jí)作用域的出現(xiàn)

通過(guò)var聲明的變量存在變量提升的特性:

if (condition) {
    var value = 1;
}
console.log(value);

如果condition為false杆煞,結(jié)果并沒(méi)有報(bào)錯(cuò)娩嚼,而是輸出undefined, 因?yàn)樽兞刻嵘脑颍?代碼相當(dāng)于:

var value;
if (condition) {
    value = 1;
}
console.log(value);

除此之外蘑险,在for循環(huán)中:

for (var i = 0; i < 10; i++) {
    ...
}
console.log(i); // 10

即便循環(huán)已經(jīng)結(jié)束了,我們依然可以訪問(wèn)i的值岳悟。
為了加強(qiáng)對(duì)變量生命周期的控制佃迄, ES6引入了塊級(jí)作用域。
塊級(jí)作用域存在于:

  • 函數(shù)內(nèi)部
  • 塊中()字符和{}之間的區(qū)域
let 和 const

塊級(jí)聲明用于聲明在指定塊的作用域之外無(wú)法訪問(wèn)的變量贵少。
let 和 const 都是塊級(jí)聲明的一種呵俏。
let 和 const 的特點(diǎn):
1. 不會(huì)被提升

if (false) {
    let value = 1;
}
console.log(value); // Uncaught ReferenceError: value is not defined

2. 重復(fù)聲明報(bào)錯(cuò)

var value = 1;
let value = 2; // Uncaught SyntaxError: Identifier 'value' has already been declared

3. 不綁定全局作用域
當(dāng)在全局作用域中使用var聲明的時(shí)候,會(huì)創(chuàng)建一個(gè)新的全局變量作為全局對(duì)象的屬性

var value = 1;
console.log(window.value); // 1

然而let和const不會(huì)

let value = 1;
console.log(window.value); // undefined

再來(lái)說(shuō)下let和const的區(qū)別
const用于聲明常量滔灶,其值一旦被設(shè)定不能再被修改普碎,否則會(huì)報(bào)錯(cuò)。
但是const聲明復(fù)雜類型時(shí)录平,const不允許修改綁定麻车,但允許修改值缀皱, 比如當(dāng)用const聲明對(duì)象時(shí):

const data = {
    value: 1
}

// 沒(méi)有問(wèn)題
data.value = 2;
data.num = 3;

// 報(bào)錯(cuò)
data = {}; // Uncaught TypeError: Assignment to constant variable.
臨時(shí)死區(qū)

let 和 const聲明的變量不會(huì)被提升到作用域頂部,如果在聲明之前訪問(wèn)這些變量动猬,會(huì)導(dǎo)致報(bào)錯(cuò):

console.log(typeof value); // Uncaught ReferenceError: value is not defined
let value = 1;

這是因?yàn)镴avaScript引擎在掃描代碼發(fā)現(xiàn)變量聲明時(shí)啤斗,要么將它們提升到作用域頂部(遇到var聲明),要么將聲明放在TDZ中(臨時(shí)死區(qū)中)(遇到let和const聲明)赁咙。訪問(wèn)TDZ中的變量會(huì)觸發(fā)運(yùn)行時(shí)錯(cuò)誤钮莲。只有執(zhí)行過(guò)變量聲明語(yǔ)句后,變量才會(huì)從TDZ中移除彼水,然后方可訪問(wèn)

var value = "global";

// 例子1
(function() {
    console.log(value);

    let value = 'local';
}());

// 例子2
{
    console.log(value);

    const value = 'local';
};

兩個(gè)例子中臂痕,結(jié)果都報(bào)錯(cuò):Uncaught ReferenceError: value is not defined, 就是因?yàn)門DZ緣故

總之猿涨,在代碼塊內(nèi)握童,使用let命令聲明變量之前股淡,該變量都是不可用的痢畜。 這在語(yǔ)法上稱為“暫時(shí)性死區(qū)”

循環(huán)中的塊級(jí)作用域
var funcs = [];
for (var i = 0; i < 3; i++) {
    funcs[i] = function () {
        console.log(i);
    };
}
funcs[0](); // 3

解決方案:

var funcs = [];
for (var i = 0; i < 3; i++) {
    funcs[i] = (function(i){
        return function() {
            console.log(i);
        }
    }(i))
}
funcs[0](); // 0

ES6的let為這個(gè)問(wèn)題提供了新的解決方案:

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

問(wèn)題在于,上面講了let不提升滑蚯,不能重復(fù)聲明俺附,不能綁定全局作用域等特性肥卡,可是為什么
在這里能打印出i值呢?

如果是不重復(fù)聲明事镣,在循環(huán)第二次的時(shí)候步鉴, 又用let聲明了i, 應(yīng)該報(bào)錯(cuò)呀氛琢, 就算因?yàn)槟撤N原因阳似,重復(fù)聲明不報(bào)錯(cuò),一遍一遍迭代, i 的值最終還是應(yīng)該是3呀畜吊,因?yàn)閒or循環(huán)設(shè)置循環(huán)變量那部分是一個(gè)單獨(dú)的作用域,就比如:

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

如果我們把let 改成 var 呢

for (var i = 0; i < 3; i++) {
  var i = 'abc';
  console.log(i);
}
// abc
// 這里輸出一次一個(gè)abc, 是因?yàn)閒or()里面使用var時(shí)沒(méi)有在()內(nèi)形成封閉的作用域,i 被循環(huán)體賦值成‘a(chǎn)bc’,  !!('abc' < 3) === false 循環(huán)停止了恋腕。

究其原因获高,是因?yàn)閘et聲明在循環(huán)內(nèi)部的行為是標(biāo)準(zhǔn)中專門定義的,不一定就與let的不提升特性有關(guān)币狠。其實(shí)肛炮,在早期的let實(shí)現(xiàn)中就不包含這一行為碍扔。

在for循環(huán)中使用let和var悲幅,底層會(huì)使用不同的處理方式。
那么當(dāng)使用let的時(shí)候底層到底發(fā)生了什么

簡(jiǎn)單的來(lái)說(shuō), 就是在 for(let i=0; i<3; i++)中杰妓,即圓括號(hào)之內(nèi)建立了一個(gè)隱藏的作用域验靡,這就可以解釋為什么:

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

然后每次迭代循環(huán)時(shí)都創(chuàng)建一個(gè)新變量,并以之前迭代中同名變量的值將其初始化怔锌。 這樣對(duì)于下面這個(gè)一段代碼:

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

就相當(dāng)于

// 偽代碼
(let i = 0) {
    funcs[0] = function() {
        console.log(i)
    };
}

(let i = 1) {
    funcs[1] = function() {
        console.log(i)
    };
}

(let i = 2) {
    funcs[2] = function() {
        console.log(i)
    };
};

當(dāng)執(zhí)行函數(shù)的時(shí)候亚情,根據(jù)詞法作用域就可以找到正確的值裳瘪, 其實(shí)你也可以理解為let聲明模仿了閉包的做法來(lái)簡(jiǎn)化了循環(huán)過(guò)程。

循環(huán)中的let 和 const

如果我們把let 改成 const 呢

var funcs = [];
for (const i = 0; i < 10; i++) {
    funcs[i] = function () {
        console.log(i);
    };
}
funcs[0](); // Uncaught TypeError: Assignment to constant variable.

結(jié)果會(huì)報(bào)錯(cuò),因?yàn)殡m然每次都創(chuàng)建一個(gè)新的變量,然而我們卻在迭代中嘗試修改const的值,所以最終會(huì)報(bào)錯(cuò)溢十。

for in循環(huán)
var funcs = [], object = {a: 1, b: 1, c: 1};
for (var key in object) {
    funcs.push(function(){
        console.log(key)
    });
}

funcs[0]() // 'c'

如果把var 改成let 或const
都輸出‘c’, 為什么這里改成const不會(huì)報(bào)錯(cuò)乌庶, 因?yàn)樵趂or in循環(huán)中,每次迭代不會(huì)修改已有的綁定,而是會(huì)創(chuàng)建一個(gè)新的綁定

balel

在babel中是如何編譯let和const的呢, 我們來(lái)看看編譯后的代碼:

let value = 1;

編譯為:

var value = 1;

我們可以看到babel直接將let編譯成var, 如果是這樣的話,那么看下面例子

if (false) {
    let value = 1;
}
console.log(value); // Uncaught ReferenceError: value is not defined

如果還是直接編譯成var, 打印結(jié)果肯定是undefined, 然而babel很聰明編譯成了:

if (false) {
    var _value = 1;
}
console.log(value);

在直觀寫個(gè)例子

let value = 1;
{
    let value = 2;
}
value = 3;

編譯成:

var value = 1;
{
    var _value = 2;
}
value = 3;

本質(zhì)是一樣的交排, 就是改變變量名,是內(nèi)外層的變量名稱不一樣。
那const的修改值時(shí)報(bào)錯(cuò),以及重復(fù)聲明報(bào)錯(cuò)是怎么實(shí)現(xiàn)的呢钱雷, 其實(shí)就是在編譯的時(shí)候直接給你報(bào)錯(cuò)...
那循環(huán)中的let聲明呢

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

babel巧妙的編譯成了

var funcs = [];

var _loop = function _loop(i) {
    funcs[i] = function () {
        console.log(i);
    };
};

for (var i = 0; i < 10; i++) {
    _loop(i);
}
funcs[0](); // 0
最佳實(shí)踐

在我們開發(fā)的時(shí)候钞支,可能認(rèn)為應(yīng)該默認(rèn)使用let而不是var, 這種情況下撼嗓,對(duì)于需要寫保護(hù)的變量要使用const. 然而另一種做法日益普及: 默認(rèn)使用const, 只有當(dāng)確實(shí)需要改變變量的值的時(shí)候才使用let. 這是因?yàn)榇蟛糠值淖兞康闹翟诔跏蓟蟛粦?yīng)再改變礁遣,而預(yù)料之外的變量的改變是很多bug的源頭浅碾。

參考https://github.com/mqyqingfeng/Blog/issues/82

面試題:
var和let/const的區(qū)別
1.塊級(jí)作用域
2.不存在變量提升
3.暫時(shí)性死區(qū)
4.不可重復(fù)聲明
5.let const聲明的全局變量不會(huì)掛在頂層對(duì)象下面

const 需注意倆點(diǎn):

  1. const 聲明之后必須馬上賦值根暑,否則會(huì)報(bào)錯(cuò)
    2.const簡(jiǎn)單類型一旦聲明就不能再更改,復(fù)雜類型(數(shù)組缰犁,對(duì)象等)指針指向的地址不能更改淳地,但是內(nèi)部數(shù)據(jù)可以更改颇象。
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末扰魂,一起剝皮案震驚了整個(gè)濱河市晃听,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌鹃答,老刑警劉巖,帶你破解...
    沈念sama閱讀 222,183評(píng)論 6 516
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件羞酗,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡紊服,警方通過(guò)查閱死者的電腦和手機(jī)裤园,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,850評(píng)論 3 399
  • 文/潘曉璐 我一進(jìn)店門撤师,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)剂府,“玉大人拧揽,你說(shuō)我怎么就攤上這事∠僬迹” “怎么了淤袜?”我有些...
    開封第一講書人閱讀 168,766評(píng)論 0 361
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)衰伯。 經(jīng)常有香客問(wèn)我铡羡,道長(zhǎng),這世上最難降的妖魔是什么意鲸? 我笑而不...
    開封第一講書人閱讀 59,854評(píng)論 1 299
  • 正文 為了忘掉前任烦周,我火速辦了婚禮,結(jié)果婚禮上怎顾,老公的妹妹穿的比我還像新娘读慎。我一直安慰自己,他們只是感情好槐雾,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,871評(píng)論 6 398
  • 文/花漫 我一把揭開白布夭委。 她就那樣靜靜地躺著,像睡著了一般募强。 火紅的嫁衣襯著肌膚如雪株灸。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 52,457評(píng)論 1 311
  • 那天擎值,我揣著相機(jī)與錄音慌烧,去河邊找鬼。 笑死鸠儿,一個(gè)胖子當(dāng)著我的面吹牛屹蚊,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播捆交,決...
    沈念sama閱讀 40,999評(píng)論 3 422
  • 文/蒼蘭香墨 我猛地睜開眼淑翼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了品追?” 一聲冷哼從身側(cè)響起玄括,我...
    開封第一講書人閱讀 39,914評(píng)論 0 277
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎肉瓦,沒(méi)想到半個(gè)月后遭京,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體胃惜,經(jīng)...
    沈念sama閱讀 46,465評(píng)論 1 319
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,543評(píng)論 3 342
  • 正文 我和宋清朗相戀三年哪雕,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了船殉。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 40,675評(píng)論 1 353
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡斯嚎,死狀恐怖利虫,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情堡僻,我是刑警寧澤糠惫,帶...
    沈念sama閱讀 36,354評(píng)論 5 351
  • 正文 年R本政府宣布,位于F島的核電站钉疫,受9級(jí)特大地震影響硼讽,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜牲阁,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 42,029評(píng)論 3 335
  • 文/蒙蒙 一固阁、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧城菊,春花似錦备燃、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,514評(píng)論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至法瑟,卻和暖如春冀膝,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背霎挟。 一陣腳步聲響...
    開封第一講書人閱讀 33,616評(píng)論 1 274
  • 我被黑心中介騙來(lái)泰國(guó)打工窝剖, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人酥夭。 一個(gè)月前我還...
    沈念sama閱讀 49,091評(píng)論 3 378
  • 正文 我出身青樓赐纱,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親熬北。 傳聞我的和親對(duì)象是個(gè)殘疾皇子疙描,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,685評(píng)論 2 360

推薦閱讀更多精彩內(nèi)容