ES6之---讀懂let才能少踩坑

開聊吧!(前面一小部分大佬們能夠自動忽略)

首先,打臉個人無知!javascript

在剛接觸ES6的時候,我就「覺得」我理解了let。而後漫長的自學道路上,let一次又一次的讓我認識到了本身的無知。java

但願寫了這篇文章以後能用個人無知,讓在這條道路上的人能少踩些坑。


初識let

和不少人同樣,在據說了ES6很好用以後,就快馬加鞭的去學習這門充滿着語法糖的東西。開始抱着emmmm快速把es6過一遍的念頭去了菜鳥教程(由於以爲這裏的教程很簡潔,可是但願你們之後慎重選擇初學教程!!!)es6

如圖所示,這就是我對他的初始理解---一個解決javascript沒有塊級做用域的東西。嗯....就是這樣也僅此而已。

let淺解

《1》let 聲明的變量的做用域是塊級的;

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

a // ReferenceError: a is not defined.
b // 1
複製代碼

用法相似於var,可是所聲明的變量,只在let命令所在的代碼塊內有效.面試

說說這個「塊級做用域」裏面的坑吧!!!

首先,用阮大神的一個示例來講說過第一個坑數組

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

輸出幾呢?是6嗎? 答案是10bash

爲何呢?由於上面代碼中,變量i是var命令聲明的,在全局範圍內都有效,因此全局只有一個變量i。每一次循環,變量i的值都會發生改變,而循環內被賦給數組a的函數內部的console.log(i),裏面的i指向的就是全局的i。也就是說,全部數組a的成員裏面的i,指向的都是同一個i,致使運行時輸出的是最後一輪的i的值,也就是 10。閉包

那麼怎麼解決呢? ES6以前的解決方式--構造閉包造成不銷燬的做用域保存變量異步

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

ES6的解決方式函數

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

代碼中,變量i是let聲明的,當前的i只在本輪循環有效,因此每一次循環的i其實都是一個新的變量,因此最後輸出的是6。 用ES5的代碼描述就是這樣的!oop

"use strict";

var a = [];

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

for (var i = 0; i < 10; i++) {
  _loop(i);
}
a[6](); // 6
複製代碼

再來一個日常碼代碼常常會遇到的,面試也會常常遇到。

var liList = document.querySelectorAll('li') // 共5個li
for( var i=0; i<liList.length; i++){
  liList[i].onclick = function(){
    console.log(i)
  }
}
複製代碼

打印多少呢?0,1,2,3,4 仍是 5,5,5,5,5? 相信你們你們應該都知道依次點擊 li 會打印出 5 個 5。

若是把 var i 改爲 let i,就會分別打印出 0、一、二、三、4:

var liList = document.querySelectorAll('li') // 共5個li
for( let i=0; i<liList.length; i++){
  liList[i].onclick = function(){
    console.log(i)
  }
}
複製代碼

闡述一下個人理解吧!

  • for( let i = 0; i< 5; i++) 這句話的圓括號之間,有一個隱藏的做用域。
  • for( let i = 0; i< 5; i++) { 循環體 } 在每次執行循環體以前,JS 引擎會把 i 在循環體的上下文中從新聲明及初始化一次。
  • 用ES5的代碼描述就是這樣的!
'use strict';

var liList = document.querySelectorAll('li'); // 共5個li

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

for (var i = 0; i < liList.length; i++) {
  _loop(i);
}
複製代碼

總結:但願你們在已有遇到跟循環 異步事件之類的問題的時候多注意下,不要掉坑裏了☺

《2》let 聲明的變量不存在變量提高,有暫時性死區;

先來解釋解釋這兩個吧

變量提高

console.log(foo); // 輸出undefined
console.log(bar); // 報錯ReferenceError

var foo = 2;
let bar = 2;
複製代碼

let不像var那樣會發生「變量提高」現象。因此,變量必定要在聲明後使用,不然報錯。

暫時性死區

let x = 'global'
{
  console.log(x) // Uncaught ReferenceError: x is not defined
  let x = 1
}
複製代碼

存在全局變量x,可是塊級做用域內let又聲明瞭一個局部變量x,致使後者綁定這個塊級做用域,因此在let聲明變量前,對tmp賦值會報錯。

在詳細的解釋這個問題以前,咱們先來弄懂ES6以前,咱們的做用域裏的變量預解釋。

var a = 1;
function b(params) {
    var c = 2; 
    console.log(params);
}

console.log(a);
b(3);
複製代碼

紅色框框中的部分是在b函數被調用的時候纔會按照預解釋規則一步步產生,如今寫出來是爲了方便大家理解。正由於b函數被預先聲明且賦值了,因此在b函數以前調用他就能獲得結果,代碼以下,相信你又學到很多吧!

b(3);
function b(params) {
    var c = 2; 
    console.log(params);
}

複製代碼

而後就是函數執行的第二步了

夠清楚!!!!夠明瞭吧!!!

這個咱們弄清楚了 就再把let的說一遍 那麼上面的問題你天然就豁然開朗了。

關於let的是這樣的

<1>找到全部用 let 聲明的變量,在環境中「建立」這些變量

<2>開始執行代碼(注意如今尚未初始化也沒有僞裝賦值爲undefined)

<3>執行 let聲明的變量時,將 該變量 「初始化直接賦值」

如今關於上面的不存在變量提高,有暫時性死區你應該都懂了吧。關於暫時性死區是由於let聲明的變量有塊級做用域並且沒有變量提高因此就產生了

也來看看暫時性死區在es6以前的環境裏是怎樣的吧。

//es6中
let x = 'global'
{
  console.log(x) // Uncaught ReferenceError: x is not defined
  let x = 1
}

//以前版本的環境中
'use strict';

var x = 'global';
{
  console.log(_x); // Uncaught ReferenceError: x is not defined
  var _x = 1;
}

複製代碼

《3》let 不能重複聲明已存在的變量

這個知識點就基本沒啥坑了 本身注意點兒就好

// 報錯
function () {
  let a = 10;
  var a = 1;
}

// 報錯
function () {
  let a = 10;
  let a = 1;
}
複製代碼

總結

總共也就三個知識點:徹底掌握仍是要多用!!!

一、let的塊級做用域!

二、let聲明的變量不存在變量提高,有暫時性死區!

三、let聲明的變量不容許重複聲明,不管重複用var或者其餘聲明都不行!

let的故事就這樣暫時說完啦(瘋狂求有位大佬來帶帶我啊) 不知不覺又晚上10點多了 該回寢室了 哈哈 若是學到了就給我個當心心吧 哈哈哈

相關文章
相關標籤/搜索