在线观看不卡亚洲电影_亚洲妓女99综合网_91青青青亚洲娱乐在线观看_日韩无码高清综合久久

鍍金池/ 教程/ HTML/ let 和 const
生成器
箭頭函數(shù)
繼續(xù)迭代器
未來前景
簡介
非結(jié)構(gòu)化賦值
迭代器和 for-of 循環(huán)
代理
符號
模版字符串
let 和 const
容器
模塊

let 和 const

下面我想要介紹的是一個謙卑的驚世之作。

在 1995 年布蘭登·艾克設(shè)計 JavaScript 的第一個版本時,他得到了許多錯誤,包括有些從那以后已經(jīng)成為語言的一部分的東西,比如如果你不慎把 Date object 和 objects 相乘,它會自動轉(zhuǎn)換成 NaN。然而,他所做對的事情在事后來看是極其重要的:對象、原型、有詞法作用域的一流功能、可變性默認(rèn)。語言具有良好的骨骼框架。這比以前實現(xiàn)的任何語言都要好。

不過,布蘭登做了一個特殊的決策,也是本章的重點內(nèi)容——一個我認(rèn)為可以被公開定性為錯誤的決定。這是一個小事情,一個微妙的事情。你可能已經(jīng)使用這個語言多年但從未注意到它。但是它很重要,因為這個錯誤存在于現(xiàn)在我們所認(rèn)為的“好的部分”里面。

它必須與變量一起工作。

問題#1:塊不是作用域

這個規(guī)則聽起來很無辜。在一個 JS 函數(shù)內(nèi)聲明的一個 var 的作用域是那個函數(shù)的整個主體。但是這里有兩個方式可能讓它產(chǎn)生無病呻吟的效果。

一是在塊中聲明的變量的作用域不只是這個塊。它是整個函數(shù)。

你可能從未意識到這個。我恐怕這是你容易忽略的東西之一。我們來看一個場景,它導(dǎo)致了一個棘手的問題。

你現(xiàn)在的代碼中有一個變量,名為 t:

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

到目前為止一切都很好?,F(xiàn)在你想要添加保齡球速度測量,所以你在內(nèi)部回調(diào)函數(shù)中加入了一個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 的變量?,F(xiàn)在來看,之前工作地好好的“變量 t”指向新的內(nèi)部變量,而不再是現(xiàn)有的外部變量。

JavaScript 中 var 的作用域就像是 Photoshop 中的油漆桶工具。它同時向聲明的前后兩個方向延伸,直到到達(dá)一個函數(shù)邊界。因為這個變量 t 的作用域向后延伸的太長了,所以我們在輸入函數(shù)時必須盡快創(chuàng)建它。這就叫做變量提升(hoisting)。我喜歡把這想象成 JS 引擎利用一個微小的代碼起重機把每個 var 和函數(shù)提升到封閉函數(shù)頂。

現(xiàn)在,變量提升有了它的優(yōu)點。沒有了它,許多在全球作用域內(nèi)工作良好的 cromulent 技術(shù)就無法在IIFE內(nèi)工作。但在這種情況下,變量提升會帶來一個討厭的錯誤:你所有用了 t 的計算都會產(chǎn)生 NaN。這很難追查,特別是當(dāng)你的代碼比這個玩具例子更長的時候。

添加一個新的代碼塊會給該塊之前的代碼帶來一個難以理解的錯誤。只有我有這樣的問題,還是這真的很奇怪?我們不想影響到前面的原因。

但是與第二個 var 問題相比,這真的是小菜一碟。

問題#2:循環(huán)中的變量過度分享

你可以猜猜你運行這個代碼時會發(fā)生什么。這非常容易想。

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

如果你一直關(guān)注 ES6,你會知道我喜歡用 alert() 作為示例代碼。也許你也知道 alert() 是一個糟糕的 API。它是同步的,所以當(dāng)一個 alert 可見時,輸入的事件不會傳遞。你的 JS 代碼——其實是你的整個界面——在用戶點擊 OK 之前基本暫停了。

你在 web 頁面上做的所有的操作,都可以使用 alert() 作為錯誤提示。我使用它是因為我認(rèn)為所有這些相同的事情會讓 alert() 成為一個有效的調(diào)試工具。

不過,我可能會被說服去放棄所有不良行為…如果這意味著我可以做一只會說話的貓。

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);
}

來看看這個代碼在操作中的不正確工作!

但有些事情是錯誤的。貓會說三次“未定義”,而不是依次說出這三條信息。

你能找出下圖的一個 “bug” 嗎?

http://wiki.jikexueyuan.com/project/es-six-deeply/images/const.jpg" alt="" />圖片來源:內(nèi)維爾·賽沃瑞"

這里的問題是只有一個變量 i。它被循環(huán)本身和三個超時回調(diào)共享。當(dāng)循環(huán)完成運行,i 的值是 3(因為 messages.length 是 3)而且所有的回調(diào)函數(shù)都還沒有被回調(diào)。

所以當(dāng)?shù)谝粋€超時觸發(fā)并調(diào)回 cat.say(messages[i]) 是用了未被定義的 messages[3]。

解決這個問題的方法有許多(這里有一個),但這是一個 var 作用域規(guī)則帶來的二次問題。如果從一開始就沒有過這種問題,那便真是太好了。

let 是新型 var

在大多數(shù)情況下,JavaScript(還有其他編程語言,但尤其是 JavaScript)中的設(shè)計錯誤無法被修復(fù)。向后兼容性意味著永遠(yuǎn)不改變現(xiàn)有的 JS 代碼在網(wǎng)絡(luò)上的行為。即使是標(biāo)準(zhǔn)委員會也沒有辦法修復(fù) JavaScript 自動插入分號這件怪事。瀏覽器制造商根本無法實現(xiàn)突破性改革,因為那種改變根本就是虐待他們的用戶。

所以大約十年前 Brendan Eich 決定解決這個問題時,也就只有一個方法。

他添加了新的關(guān)鍵詞 let,let 就像 var 一樣可用來聲明變量,但它擁有更好的作用域規(guī)則。

它看起來是這樣的:

let t = readTachymeter();

let 和 var 是不同的,如果你只在你的代碼中做了一個全局搜索替換,可能就會由于 var 的原因破壞了你的部分代碼(可能是無意地)。但在大多數(shù)情況下,在新的 ES6 代碼中你應(yīng)該使用 let 代替 var。所以我們的口號是:“l(fā)et 是新型 var”。

let 和 var 之間的真正區(qū)別是什么?很高興你問了這個問題!

  • let 變量是有塊作用域的。用 let 定義的變量作用域只是封閉塊,而不是整個封閉函數(shù)?,F(xiàn)在仍有 let 變量提升,但是不再是隨意提升。在 TherunTowerExperiment 例子中,可以用把 var 替換成 let 的方法來進(jìn)行簡單修復(fù)。如果你在每個地方都是使用的 let,你就不會有這種錯誤。

  • 全局 let 變量不是全局對象的屬性。也就是說,你不能通過寫 window.variableName 訪問它們。相反,它們存在于一個無形塊作用域內(nèi),我們可以想象成一個網(wǎng)頁運行的所有的 JS 代碼都被封閉在了一起。

  • for (let x...)循環(huán)形式在每個迭代中都為 x 創(chuàng)建了一個新鮮的捆綁。

這是一個非常微妙的差異。這意味著如果一個 for(let...)循環(huán)執(zhí)行多次并且該循環(huán)包含一個閉包,就像我們之前舉的會說話的貓的例子,每個閉包都會捕獲一個不同副本的循環(huán)變量而不是每個閉包都捕獲相同的循環(huán)變量。所以示例會說話的貓也可以通過用 let 替換 var 來完成修復(fù)。

這適用于所有三種 for 循環(huán):for–of,for–in 和加上分號的老式 C 類。

  • 在聲明之前使用一個 let 變量是錯誤的。在控制流到達(dá)有聲明的代碼行之前變量是未初始化的。例如:
function update() {
  console.log("current time:", t);  // ReferenceError
  ...
  let t = readTachymeter();
}

這個規(guī)則是幫助你定位錯誤。你會在有問題的代碼上獲得一個提醒而不是結(jié)果 NaN。

未初始化變量在作用域內(nèi)的時期稱為暫時性死區(qū)。我一直在等待這個激發(fā)性的術(shù)語可以躍位到科幻小說。但是什么都還沒有發(fā)生。

(脆脆的性能細(xì)節(jié):在大多數(shù)情況下,你可以說出是否聲明已經(jīng)運行了或只是在查看代碼,所以 JavaScript 引擎真的不需要在每次變量訪問時都進(jìn)行一次額外檢查來確保它已經(jīng)初始化了。然而有時在閉包內(nèi)這并不清楚。在這種情況下 JavaScript 引擎會進(jìn)行運行時檢查。這代表與 var 相比 let 需要更長的訪問時間。)

(交替全局作用域細(xì)節(jié):在某些程序設(shè)計語言中,變量的作用域在聲明點開始,而不是到達(dá)后面覆蓋整個封閉塊。標(biāo)準(zhǔn)委員考慮過為 let 使用這個作用域規(guī)則。但這樣使用 t 就會導(dǎo)致引用錯誤,隨后的 let t 就不在作用域中了,所以它根本不會再查閱那個變量。它可以在一個封閉作用域中查閱 t,但這個方法在有閉包或有函數(shù)變量提升的情況下不好用,所以它最終被拋棄了。)

  • 用 let 重新聲明變量是語法錯誤。

這條規(guī)則也有助于你發(fā)現(xiàn)微小錯誤。但這是在你嘗試 let-to-var 轉(zhuǎn)換時容易導(dǎo)致問題的不同之處,因為它提供了甚至全局的 let 變量。

如果你有一些都是聲明相同全局變量的腳本,你最好使用 var。如果你用了 let,任何腳本在二次加載的時候都會出現(xiàn)錯誤無法運行。

或者運用 ES6 模塊。但這又是另一個故事了。

(語法細(xì)節(jié):let 是一個嚴(yán)格模式下的保留詞。在非嚴(yán)格模式的代碼中,為了向后兼容,你仍然可以聲明變量、函數(shù)和名為 let 的實參——你可以寫 var let = 'q';!,而不是你原本想寫的那樣。let let; 也是不允許的。)

除了這些差異,let 和 var 幾乎是一樣的。它們都支持聲明多個用逗號隔開的變量,例如他們都支持解構(gòu)。

注意,類聲明的行為和 let 很像而和 var 不像。如果你加載了一個包含多次類的腳本,在第二次時你會因為沒有重新聲明類而得到一個錯誤。

Const

對了,還有一件事!

ES6 還引入了第三級關(guān)鍵字,你可以在 let 后使用:const。

用 const 聲明的變量就像 let,除非你不能指定它們或除非你在它們的聲明點進(jìn)行了聲明,這樣會產(chǎn)生語法錯誤。

const MAX_CAT_SIZE_KG = 3000; //
上一篇:容器下一篇:未來前景