深切ES6:let和const

ES6 In Depth是一系列關於在ECMAScript規範的第六版中到場JavaScript編程言語的新功用,簡稱ES6。

我本日想談的這個特徵既簡樸又使人覺得欣喜。

當Brendan Eich在1995年設想了JavaScript的第一個版本時,其中有許多題目,包括自此以後一直是該言語的一部份的東西,比方Date對象和對象在乎外埠相乘時會自動轉換為NaN。然則,預先看來,他所做的事變是非常主要的事變:_objects_; _prototypes_;_function_和_作用域_;可變數據類型。這門言語有很好的構造。比任何人一最先意想到的要好。

儘管如此,Brendan 照樣做了一個迥殊的設想決議 – 這個決議我以為是一個毛病。這是一件小事。一個玄妙的東西。您能夠會運用該言語多年,以至沒有注重到它。但它很主要,因為這個毛病在我們如今以為是“好的部份”的言語中。

它與變量有關

題目#1:塊不是局限

劃定規矩聽起來很合理: JS函數中聲明的var的作用域是該函數的全部主體。 然則有兩種體式格局會致使差別的效果。

一個是塊中聲明的變量局限不僅僅是塊。是全部函數。

你之前能夠從未注重到這一點。生怕這是你沒法看不到的東西之一。讓我們經由歷程一個會致使辣手題目的場景來申明這個bug。

假定您有一段代碼是運用名為t的變量:

function runTowerExperiment(tower, startTime) {
 var t = startTime;
tower.on("tick", function () {
   ... code that uses t ...
 });
... more code ...
}

到目前為止,一切都很好。如今你想增加保齡球速率丈量,所以你增加一個if語句到內部回調函數。

function runTowerExperiment(tower, startTime) {
 var t = startTime;
tower.on("tick", function () {
   ... code that uses t ...
   if (bowlingBall.altitude() <= 0) {
     var t = readTachymeter();
...
   }
 });
... more code ...
}

你無意中增加了第二個名為t的變量。如今,在“運用t的代碼”之前事情得很好,t指的是新的內部變量t,而不是現有的外部變量。

JavaScript中var的局限就像Photoshop中的繪畫東西。它從聲明的兩個方向延長,向前和向後,而且它繼承前進,直到它抵達函數邊境。因為這個變量的局限向後延長,所以只需我們進入函數就必須建立它。這被稱為提拔。我喜好設想JS引擎將每一個變量和函數用一個小代碼起重機提拔到封閉函數的頂部。

如今,變量提拔有其優點。沒有它,許多在全局局限內事情得很好的圓滿實用的手藝在 IIFE內部不起作用。然則在這類情況下,提拔造成了一個使人討厭的bug:一切運用t的盤算都將最先天生NaN。這也很難清查,迥殊是假如你的代碼比這個例子更大的時刻。

增加一個新的代碼塊致使該塊之前的代碼中湧現神奇毛病。我們不願望效果先於緣由。

但與第二個var題目比擬,這還只是一個小題目。

題目#2:輪迴中的變量眾多

你能夠猜想當你運轉這段代碼時會發作什麼。這非常簡樸:

var messages = ["Hi!", "I'm a web page!", "alert() is fun!"];
 for (var i = 0;i < messages.length;i++) {
   alert(messages[i]);
}

假如你一直在關注這個系列,你就曉得我喜好用alert()作為代碼。或許你也曉得alert()是一個蹩腳的API。它是同步的。因而,雖然警報可見,但輸入事宜不會通報。您的JS代碼 – 實際上是您的全部UI – 基本上都邑停息,直到用戶單擊肯定。(譯者: alert具有阻塞性)

一切這些都讓alert()險些成為了你想要在網頁中做的任何事變的毛病挑選。我運用它是因為我以為一切這些雷同的東西都使alert()成為一個很好的教授教養東西。

儘管如此,我照樣能夠壓服自身摒棄一切這些愚笨和不良行動……假如這意味着我能夠做一個措辭的貓。

var messages = ["Meow!", "I'm a talking cat!", "Callbacks are fun!"];
for (var i = 0;
i < messages.length;
i++) {
 setTimeout(function () {
   cat.say(messages[i]);
}, i * 1500);
}

看到這個代碼事情不正確!

B但有些事變是錯的。貓沒有按遞次說出一切三條音訊,而是三次說“undefined”。

你能發明毛病嗎?

這裏的題目是,我只要一個變量。它由輪迴自身和一切三個超時回調同享。當輪迴完畢運轉時,i的值為3(因為messages.length為3),而且還沒有挪用任何回調。(譯者:能夠去相識一下Event loop)

所以當第一次超時觸發並挪用cat.say(messages [i])時,它運用音訊[3]。誰人當然是undefined的。

有許多要領能夠處理這個題目(這裡是一個),這是由var局限劃定規矩引發的第二個題目。

let 是新的 var

大多數情況下,JavaScript中的設想毛病(其他編程言語,迥殊是JavaScript)也沒法修復。向後兼容意味着永久不要轉變Web上現有JS代碼的行動。縱然規範委員會也沒有權利,比方用JavaScript的自動分號插進去修改新鮮的怪癖。瀏覽器製造商基礎不會實行突破性的轉變,因為這類轉變懲罰了用戶。

約莫十年前,當Brendan Eich決議處理這個題目時,實際上只要一種要領能夠處理這個題目。

他增加了一個新的關鍵字let,它能夠用來聲明變量,就像var一樣,然則具有更好的局限劃定規矩。

它看起來像如許:

let t = readTachymeter();

或則如許:

for (let i = 0;i < messages.length;i++) {
 ...
}

let和var是差別的,所以假如你只是在全部代碼中舉行全局搜刮和替代,那能夠會損壞你的代碼的一部份(能夠無意)依賴於var的怪癖。但絕大多數情況下,在新的ES6代碼中,您應當停止運用var並運用let來替代。因而標語是:“let is the new var”。

let和var之間終究有什麼區分?很愉快你有如許的疑問!

  • let 變量是 block-scope. 用let聲明的變量的作用域就是封閉塊,而不是全部封閉函數。

runTowerExperiment示例能夠經由歷程將var更改成let來修復。假如你在任何處所運用let,你將永久不會有如許的毛病。

  • 全局let變量不是全局對象的屬性。也就是說,你不會經由歷程編寫window.variableName來接見它們。相反,他們生活在一個隱形塊的局限內,這個塊在概念上包括了一切在網頁中運轉的JS代碼。
  • (let x …)情勢的輪迴在每次迭代中為x建立一個新的綁定。.

這是一個非常玄妙的差別。這意味着假如一個for(let …)輪迴實行屢次,而且該輪迴包括一個閉包,就像我們在議論貓的例子中一樣,每一個閉包將捕捉輪迴變量的差別副本,而不是捕捉雷同的閉包輪迴變量。

所以措辭的貓例子也能夠經由歷程轉變var來處理。 這實用於一切三種for輪迴:for-of, for-in,以及帶分號的舊式C類。

  • 在聲明之前嘗試運用let變量是毛病的。 在掌握流抵達聲明的代碼行之前,該變量是未初始化的。比方:

     function update() {
     console.log("current time:", t);
     // ReferenceError
     ...
     let t = readTachymeter();
     }
     

    這條劃定規矩能夠協助你發明毛病。替代NaN效果,您會在題目所在的代碼行中發明非常。

    這個變量在局限內但未初始化的時代稱為時候盲區。

    (軟弱的機能細節:在大多數情況下,經由歷程檢察代碼能夠推斷聲明是不是運轉,所以JavaScript引擎在每次接見變量時都不須要實行分外的搜檢,以確保它已然則,在一個閉包中,有時刻不清楚,在這類情況下,JavaScript引擎會實行一次運轉時搜檢,這意味着能夠比var慢一點。)

    (軟弱的作用域局限細緻申明:在一些編程言語中,變量的局限從聲明最先,而不是向後掩蓋全部封閉塊。規範委員會斟酌運用這類局限劃定規矩來讓。如許,援用ReferenceError的t的運用基礎就不在後面的let t的局限內,所以它基礎不會援用該變量,它能夠援用在封閉局限內的at。要領在封閉或功用提拔方面效果不佳,因而終究摒棄了)。

  • 用let反覆聲明一個變量是一個SyntaxError(譯者:語法毛病)。

這條劃定規矩也能夠協助你發明眇乎小哉的毛病。儘管如此,假如嘗試舉行全局let-to-var轉換,最有能夠致使您碰到一些題目的區分,因為它以至實用於全局let變量。

假如你有幾個劇本都聲清楚明了雷同的全局變量,那末最好繼承運用var。假如您切換到let,不管哪一個劇本加載第二個將失利並湧現毛病。

或許運用ES6模塊。但那又是別的故事。

(Crunchy語法細節:let是嚴厲形式代碼中的保留字,在非嚴厲形式代碼中,為了向後兼容,您依然能夠聲明名為let的變量,函數和參數,您能夠編寫var let =’ q’)

除了這些差別以外,let和var險些是一樣的。
比方,聲明多個由逗號分開的變量,而且它們都支撐解構

請注重,class聲明的行動如let,而不是var。假如屢次加載包括類的劇本,則第二次從新聲明該類時會湧現毛病。

const(常量)

對,另有一件事!

ES6還引入了第三個關鍵字,您能夠運用let:const。

用const聲明的變量就像let,除了你不能指定給它們,除了它們被聲明的處所。這是一個SyntaxError。

const MAX_CAT_SIZE_KG = 3000;
// 🙀

MAX_CAT_SIZE_KG = 5000;
// SyntaxError
MAX_CAT_SIZE_KG++;
// nice try, but still a SyntaxError

而且,你不能聲明一個const而不給它一個值。

const theFairest;
// SyntaxError, you troublemaker

Secret agent namespace

“Namespaces are one honking great idea—let’s do more of those!” —Tim Peters, “The Zen of Python”

在ES3之前,JavaScript只要全局局限和功用局限。 (讓我們疏忽語句。)ES3引入了try-catch語句,這意味着增加一種新的作用域,僅用於catch塊中的非常變量。 ES5增加了strict eval()運用的作用域。在評價參數的默認值時,ES6增加了塊局限,for-loop局限,新的全局挪用局限,模塊局限和其他局限。

從ES3最先增加的一切分外局限關於使JavaScript的歷程和面向對象特徵像封閉一樣順暢,準確和直觀地事情以及與閉包無縫合作是必要的。或許你在本日之前從未注重過任何這些局限劃定規矩。假如是如許,言語正在做它的事情

我如今能夠運用let和const嗎?

是。要在收集上運用它們,您必須運用ES6編譯器,比方 BabelTraceurTypeScript

io.js支撐let和const,但僅限於嚴厲形式代碼。 Node.js支撐是雷同的,然則–harmony選項也是必須的。

Brendan Eich在九年前.完成了Firefox的第一個版本。該功用在規範化歷程當中舉行了完全從新設想。Shu-yu Guo 正在晉級我們的實行以相符規範,由Jeff Walden等人舉行代碼評審。

那末,我們正在家中。我們史詩般的ES6功用之旅行將完畢。在兩周內,我們將完成多是一切人最熱切期待的ES6功用。但起首,我們將在下周宣布一篇文章,擴大我們之前報導的超新功用。所以請到場我們,Eric Faust回憶一下ES6子類化的深度。

    原文作者:_小生_
    原文地址: https://segmentfault.com/a/1190000014827831
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞