摘要:很顯然,局部作用域只能在內(nèi)部訪問。第一次被稱為編譯狀態(tài),這一次的話,代碼中定義的變量就會提升。嚴格來說,我們變量的聲明已經(jīng)被移動了位置,而不是聲明的相關代碼被移動了位置。
es6有許多特別棒的特性,你可能對該語言的整體非常熟悉,但是你知道它在內(nèi)部是如何工作的嗎?當我們知道它的內(nèi)部原理以后,我們使用起來也會更加的安心一些。這里我們想逐步的引導你,讓你對其有一個更深入,更淺顯的認識。讓我們就先從es6中的變量開始講起吧。
let和const在es6中新引入了兩種方式來申明變量,我們?nèi)匀豢梢允褂脧V為傳誦的var變量(然而你不應該繼續(xù)使用它了,繼續(xù)閱讀來了解其中原因),但是現(xiàn)在我們有了兩種更牛的工具去使用:let和const。
letlet和var非常的相似,在使用方面,你可以使用完全相同的方式來聲明變量,例如:
let myNewVariable = 2; var myOldVariable = 3; console.log(myNewVariable); // 2 console.log(myOldVariable); // 3
但是實際上,他們之間有幾處明顯的不同。他們不僅僅是關鍵字變了,而且實際上它還讓會簡化我們的一些工作,防止一些奇怪的bug,其中這些不同點是:
let是塊狀作用域(我將會在文章后面著重講一下作用域相關的東西),而var是函數(shù)作用域。
let不能在定義之前訪問該變量(var是可以的,它確實是js世界中許多bug和困擾的源頭)。
let不能被重新定義。
在我們講解這些不同點之前,首先我們看一個更酷的變量:const
constconst和let非常像(跟var相比來說,他們之間有許多相同點),但是他們有一個主要的不同點:let可以重新賦值,但是const不能。因此const定義的變量只能有一個值,并且這個值在聲明的時候就會被賦值。因此我們來看下下面的例子。
const myConstVariable = 2; let myLetVariable = 3; console.log(myConstVariable); // 2 myLetVariable = 4; // ok myConstVariable = 5; //wrong - TypeError thrown
但是const是完全不可變的嗎?
有一個常見的問題:雖然變量不能被重新賦值,但是也不能讓他們真正的變?yōu)椴豢勺兊臓顟B(tài)。如果const變量有一個數(shù)組或者對象作為其值的話,你可能會像下面的代碼一樣改變它的值。
const myConstObject = {mutableProperty: 2}; // myConstObject = {}; - TypeError myConstObject.mutableProperty = 3; //ok console.log(myConstObject.mutableProperty); // 3 const myConstArray = [1]; // myConstArray = []; - TypeError myConstArray.push(2) //ok console.log(myConstArray); // [1, 2]
當然你不能用原始數(shù)據(jù)類型的值,比如string,number,boolean等,因為他們本質上是不可變的。
真正的不可變如果你想讓我們的變量真正的不可變的話,可以使用Object.freeze(), 它可以讓你的對象保持不可變。不幸的是,他僅僅是淺不可變,如果你對象里嵌套著對象的話,它依然是可變的。
const myConstNestedObject = { immutableObject: { mutableProperty: 1 } }; Object.freeze(myConstNestedObject); myConstNestedObject.immutableObject = 10; // won"t change console.log(myConstNestedObject.immutableObject); // {mutableProperty: 1} myConstNestedObject.immutableObject.mutableProperty = 10; // ok console.log(myConstNestedObject.immutableObject.mutableProperty); // 10變量的作用域
在介紹了一些基礎知識以后,下面我們要進入一個更高級的話題。現(xiàn)在我們要開始講解es5和es6變量中的第一個不同-作用域
注意:下面的例子都用的是let,它的規(guī)則在const上同樣也適用
全局變量和函數(shù)作用域變量在js中,究竟什么是作用域呢?本文不會給出一個關于作用域的完整解釋。簡單來說,變量的作用域決定了變量的可用位置。從不同的角度來看,可以說作用域是你可以在特定區(qū)域內(nèi)使用的那些變量(或者是函數(shù))的聲明。作用域可以是全局的(因此在全局作用域中定義的變量可以在你代碼中任何部分訪問)或者是局部的。
很顯然,局部作用域只能在內(nèi)部訪問。在ES6以前,它僅僅允許一種方式來定義局部作用域 - function,咱們來看一下下面的例子:
// global scope var globalVariable = 10; function functionWithVariable() { // local scope var localVariable = 5; console.log(globalVariable); // 10 console.log(localVariable); // 5 } functionWithVariable(); //global scope again console.log(globalVariable); // 10 console.log(localVariable); // undefined
上面的例子中,變量globalVariable是全局變量,所以它可以在我們代碼中的函數(shù)內(nèi)或者是其他區(qū)域內(nèi)被訪問到,但是變量localVariable定義在函數(shù)內(nèi),所以它只在函數(shù)內(nèi)可訪問。
因此,所有在函數(shù)內(nèi)創(chuàng)建的內(nèi)容都可以在函數(shù)內(nèi)被訪問到,包括函數(shù)內(nèi)部里所有的嵌套函數(shù)(可能會被嵌套多層)。在這里可能要感謝閉包了,但是在文章里我們并不打算介紹它。不過請繼續(xù)關注,因為我們在未來的博文中,會更多的介紹它。
提升簡單來說,提升是一種吧所有的變量和函數(shù)聲明“移動”到作用域的最前面的機制。讓我們看一下下面的例子。
function func() { console.log(localVariable); // undefined var localVariable = 5; console.log(localVariable); // 5 } func();
它為什么依然會正常工作呢?我們還沒有定義這個變量,但是它依然通過console.log()打印出了undefined。為什么不會報出一個變量未定義的錯誤呢?讓我們再仔細看一下。
編譯變量Javascript解析器要遍歷這個代碼兩次。第一次被稱為編譯狀態(tài),這一次的話,代碼中定義的變量就會提升。在他之后,我們的代碼就變成類似于下面的這樣子的(我已經(jīng)做了一些簡化,只展示出相關的部分)。
function func() { var localVariable = undefined; console.log(localVariable); // undefined localVariable = 5; console.log(localVariable); // 5 } func();
我們看到的結果是,我們的變量localVariable已經(jīng)被移動到func函數(shù)的作用域的最前面。嚴格來說,我們變量的聲明已經(jīng)被移動了位置,而不是聲明的相關代碼被移動了位置。我們使用這個變量并打印出來。它是undefined是因為我們還沒有定義它的值,它默認使用的undefined。
提升的例子 - 會出什么問題來讓我們看一個令人討厭的例子,我們的作用域范圍對于我們來說,是弊大于利的。也不是說函數(shù)作用域是不好的。而是說我們必須要警惕一些由于提升而引起的一些陷進。我們來看看下面的代碼:
var callbacks = []; for (var i = 0; i < 4; i++) { callbacks.push(() => console.log(i)); } callbacks[0](); callbacks[1](); callbacks[2](); callbacks[3]();
你認為輸出的值是多少呢?你猜可能是0 1 2 3,是嗎?如果是的話,對于你來說,可能會有一些驚喜。實際上,他真實的結果是4 4 4 4。等等,它到底發(fā)生了什么?我們來“編譯”一下代碼,代碼現(xiàn)在看起來就像這樣:
var callbacks; var i; callbacks = []; for (i = 0; i < 4; i++) { callbacks.push(() => console.log(i)); } callbacks[0](); callbacks[1](); callbacks[2](); callbacks[3]();
你看出問題所在了嗎?變量i在整個作用域下都是可以被訪問到的,它不會被重新定義。它的值只會在每次的迭代中不斷地被改變。然后呢,當我們隨后想通過函數(shù)調用打印它的值得時候,他實際上只有一個值 - 就是在最后一次循環(huán)賦給的那個值。
我們只能這樣了嗎?不是的
Let和Const的拯救除了定義變量的新方式以外,還引入了一種新的作用域:塊級作用域。塊就是由花括號括起來的所有的內(nèi)容。所以它可以是if,while或者是for聲明中的花括號,也可以是多帶帶的一個花括號甚至是一個函數(shù)(對,函數(shù)作用域是塊狀作用域)。let和const是塊作用域。意味著無論你在塊中無論定義了什么變量,什么時候定義的,它都不會跑到塊作用域外面去。我們來看一下下面的例子:
function func() { // function scope let localVariable = 5; var oldLocalVariable = 5; if (true) { // block scope let nestedLocalVariable = 6; var oldNestedLocalVariable = 6; console.log(nestedLocalVariable); // 6 console.log(oldNestedLocalVariable); // 6 } // those are stil valid console.log(localVariable); // 5 console.log(oldLocalVariable); // 5 // and this one as well console.log(oldNestedLocalVariable); // 6 // but this on isn"t console.log(nestedLocalVariable); // ReferenceError: nestedLocalVariable is not defined
你能看出來差別嗎?你能看出來怎么使用let來解決早些時候提出問題的嗎?我們的for循環(huán)包含一組花括號,所以它是塊作用域。所以如果在定義循環(huán)變量的時候,使用的是let或者是const來代替var的話,代碼會轉為下面的形式。注意:我實際上已經(jīng)簡化了很多,不過我確定你能理解我的意思。
let callbacks = []; for (; i < 4; i++) { let i = 0 //, 1, 2, 3 callbacks.push(() => console.log(i)); } callbacks[0](); callbacks[1](); callbacks[2](); callbacks[3]();
現(xiàn)在的每一次循環(huán)都有它自己的變量定義,所以變量不會被重寫,我們確信這行代碼可以完成讓他做的任何事情。
這是這一部分結束的例子,但是我們再看一下下面的例子,我相信你明白打印出來的值的原因,以及對應的表現(xiàn)是什么。
function func() { var functionScopedVariable = 10; let blockScopedVariable = 10; console.log(functionScopedVariable); // 10 console.log(blockScopedVariable); // 10 if (true) { var functionScopedVariable = 5; let blockScopedVariable = 5; console.log(functionScopedVariable); // 5 console.log(blockScopedVariable); // 5 } console.log(functionScopedVariable); // 5 console.log(blockScopedVariable); // 10 } func();
本文翻譯自:
https://blog.pragmatists.com/...
本文轉載自:http://www.lht.ren/article/15/
文章版權歸作者所有,未經(jīng)允許請勿轉載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉載請注明本文地址:http://m.specialneedsforspecialkids.com/yun/101656.html
摘要:規(guī)范對其是這樣進行的描述的。聲明定義了在正在運行的執(zhí)行上下文作用域內(nèi)的變量環(huán)境中的變量。在執(zhí)行時,由帶有的定義的變量被賦其設定項的的值。由于變量已經(jīng)被聲明,是可訪問的,因此會打印出正確的結果。 你想在在變量聲明之前就使用變量?以后再也別這樣做了。 新的聲明方式(let,const)較之之前的聲明方式(var),還有一個區(qū)別,就是新的方式不允許在變量聲明之前就使用該變量,但是var是可以...
摘要:聲明聲明的語法與的語法一致。總結文章都是以深入理解讀書筆記形式,大部分引用書中的定義,加上作者的理解,樣例也做了調整,所有樣例都可以放到里運行親自嘗試。 1.變量提升 使用 var 關鍵字聲明的變量,無論其實際聲明位置在何處,都會被視為聲明于所在函數(shù)的 頂部(如果聲明不在任意函數(shù)內(nèi),則視為在全局作用域的頂部)。這句話從字面上不難理解。 但是他是怎樣一個過程,為什么會這樣。當你代...
摘要:會出現(xiàn)這樣的情況是因為擁有暫時性死區(qū)。規(guī)定暫時性死區(qū)和語句不出現(xiàn)變量提升,主要是為了減少運行時錯誤,防止在變量聲明前就使用這個變量,從而導致意料之外的行為。 首先我們應該知道js引擎在讀取js代碼時會進行兩個步驟: 第一個步驟是解釋。 第二個步驟是執(zhí)行。 所謂解釋就是會先通篇掃描所有的Js代碼,然后把所有聲明提升到頂端,第二步是執(zhí)行,執(zhí)行就是操作一類的。 我們先來看個簡單的變量提升...
摘要:詞法分析對構成源程序的字符流進行掃描然后根據(jù)構詞規(guī)則識別單詞也稱單詞符號或符號。語義分析是編譯過程的一個邏輯階段語義分析的任務是對結構上正確的源程序進行上下文有關性質的審查進行類型審查,審查抽象語法樹是否符合該編程語言的規(guī)則。 1. 文章的內(nèi)容和主題 我對編譯器的深入了解起源于一條推特中的問題:Angular是如何用Angular預先編譯器(AOT)對靜態(tài)代碼進行解析工作的。在進行一些...
摘要:接著上一篇文章深入了解一的處理步驟的三個主要處理步驟分別是解析,轉換,生成。模塊是的代碼生成器,它讀取并將其轉換為代碼和源碼映射抽象語法樹抽象語法樹在以上三個神器中都出現(xiàn)過,所以對于編譯器來說至關重要。 接著上一篇文章《深入了解babel(一)》 Babel 的處理步驟 Babel 的三個主要處理步驟分別是: 解析(parse),轉換(transform),生成(generate)。對...
閱讀 2666·2023-04-25 15:22
閱讀 2837·2021-10-11 10:58
閱讀 1060·2021-08-30 09:48
閱讀 1864·2019-08-30 15:56
閱讀 1740·2019-08-30 15:53
閱讀 1106·2019-08-29 11:16
閱讀 1058·2019-08-23 18:34
閱讀 1649·2019-08-23 18:12