【面试题】【ES6】let和const命令 (面试必看)
??給大家推薦一個實用面試題庫
1、前端面試題庫 (面試必備) ? ? ? ? ? ?推薦:★★★★★
地址:前端面試題庫
1、let命令
基本用法
用法類似于var,但是所聲明的變量,只在let命令所在的代碼塊內(nèi)有效。
let只能出現(xiàn)在當(dāng)前作用域的頂層。
在for循環(huán)中,使用let聲明循環(huán)變量i,當(dāng)前的i只在本輪循環(huán)有效,每一次循環(huán)的i都是一個新的變量。
for循環(huán)還有一個特別之處,就是設(shè)置循環(huán)變量的那部分是一個父作用域,而循環(huán)體內(nèi)部是一個單獨的子作用域。
不存在變量提升現(xiàn)象
變量提升:變量可以在聲明之前使用,使用var聲明的變量會自動提升到函數(shù)作用域頂部。
==》具體爭議轉(zhuǎn)看下文“關(guān)于是否存在變量提升的爭議問題”
暫時性死區(qū)
只要塊級作用域內(nèi)存在let命令,它所聲明的變量就“綁定”(binding)這個區(qū)域,不再受外部的影響。
var tmp = 123;if (true) {tmp = 'abc'; // ReferenceError 受到let約束let tmp; } 復(fù)制代碼ES6 明確規(guī)定,如果區(qū)塊中存在let和const命令,這個區(qū)塊對這些命令聲明的變量,從一開始就形成了封閉作用域。凡是在聲明之前就使用這些變量,就會報錯。
在代碼塊內(nèi),使用let命令聲明變量之前,該變量都是不可用的。這在語法上,稱為“暫時性死區(qū)”(temporal dead zone,TDZ)
在沒有引入let前typeof是絕對安全,不會出錯的?typeof對于沒有聲明的變量會顯示undefined不會報錯,但是對于未聲明的變量會報錯ReferenceError
總之,暫時性死區(qū)的本質(zhì)就是,只要一進入當(dāng)前作用域,所要使用的變量就已經(jīng)存在了,但是不可獲取,只有等到聲明變量的那一行代碼出現(xiàn),才可以獲取和使用該變量。
不允許重復(fù)聲明
注意區(qū)分重復(fù)冗余聲明和嵌套聲明
2、塊級作用域
存在意義
ES5只有全局作用域和函數(shù)作用域
解決以下場景問題:
ES6的塊級作用域
let為JavaScript新增了塊級作用域。
ES6允許塊級作用域的任意嵌套。
內(nèi)層作用域可以定義外層作用域的同名變量。
塊級作用域的出現(xiàn),實際上使得獲得廣泛應(yīng)用的**匿名立即執(zhí)行函數(shù)表達式(匿名 IIFE)**不再必要了。
塊級作用域與函數(shù)聲明
ES5 規(guī)定,函數(shù)只能在頂層作用域和函數(shù)作用域之中聲明,不能在塊級作用域聲明。
ES6 引入了塊級作用域,明確允許在塊級作用域之中聲明函數(shù)。ES6 規(guī)定,塊級作用域之中,函數(shù)聲明語句的行為類似于let,在塊級作用域之外不可引用。
為了避免塊級作用域內(nèi)聲明的函數(shù)的處理規(guī)則對老代碼產(chǎn)生很大的影響,減輕不兼容問題,ES6允許瀏覽器有自己的行為方式,規(guī)則如下:
- 允許在塊級作用域內(nèi)聲明函數(shù)。
- 函數(shù)聲明類似于var,即會提升到全局作用域或函數(shù)作用域的頭部。
- 同時,函數(shù)聲明還會提升到所在的塊級作用域的頭部。
注意,上面三條規(guī)則只對?ES6 的瀏覽器實現(xiàn)有效,其他環(huán)境的實現(xiàn)不用遵守,還是將塊級作用域的函數(shù)聲明當(dāng)作let處理。
根據(jù)這三條規(guī)則,瀏覽器的 ES6 環(huán)境中,塊級作用域內(nèi)聲明的函數(shù),行為類似于var聲明的變量。
考慮到環(huán)境導(dǎo)致的行為差異太大,應(yīng)該避免在塊級作用域內(nèi)聲明函數(shù)。如果確實需要,也應(yīng)該寫成函數(shù)表達式,而不是函數(shù)聲明語句。
ES6 的塊級作用域必須有大括號,如果沒有大括號,JavaScript 引擎就認為不存在塊級作用域。
3、const命令
基本用法
const聲明一個只讀的常量。一旦聲明,常量的值就不能改變。
const一旦聲明變量,就必須立即初始化,不能留到以后賦值。
const的作用域與let命令相同:只在聲明所在的塊級作用域內(nèi)有效。
const命令聲明的常量也是不提升,同樣存在暫時性死區(qū),只能在聲明的位置后面使用。
const聲明的常量,也與let一樣不可重復(fù)聲明。
本質(zhì)
const實際上保證的,并不是變量的值不得改動,而是變量指向的那個內(nèi)存地址所保存的數(shù)據(jù)不得改動。
對于簡單數(shù)據(jù)類型,值就是保存變量的內(nèi)存地址【常量】;復(fù)合數(shù)據(jù)類型,變量指向的內(nèi)存地址保存的只是一個指向?qū)嶋H數(shù)據(jù)的指針。
const只能保證這個指針是固定的(即總是指向另一個固定的地址),至于它指向的數(shù)據(jù)結(jié)構(gòu)是不是可變的,就完全不能控制了。因此,將一個對象聲明為常量必須非常小心。
因此使用const聲明一個對象,對象的屬性是可變的,但是不能改變對象為另一個對象(不能改變指針的指向)。
凍結(jié)對象,無法改變對象屬性應(yīng)該使用Object.freeze方法。
var constantize = (obj) => {Object.freeze(obj);Object.keys(obj).forEach( (key, i) => {if ( typeof obj[key] === 'object' ) {constantize( obj[key] );}}); }; 復(fù)制代碼ES6聲明變量的六種方法
ES5
ES6
4、頂層對象的屬性
頂層對象,在瀏覽器環(huán)境指的是window對象,在 Node 指的是global對象。
ES5 之中,頂層對象的屬性與全局變量是等價的。
頂層對象的屬性與全局變量掛鉤帶來的問題:
從 ES6 開始,全局變量將逐步與頂層對象的屬性脫鉤。
- var命令和function命令聲明的全局變量,依舊是頂層對象的屬性;
- let命令、const命令、class命令聲明的全局變量,不屬于頂層對象的屬性。
5、globalThis對象
JavaScript 語言存在一個頂層對象,它提供全局環(huán)境(即全局作用域),所有代碼都是在這個環(huán)境中運行。
但是,頂層對象在各種實現(xiàn)里面是不統(tǒng)一的。
- 瀏覽器里面,頂層對象是window,但 Node 和 Web Worker 沒有window。
- 瀏覽器和?Web Worker?里面,self也指向頂層對象,但是 Node 沒有self。
- Node?里面,頂層對象是global,但其他環(huán)境都不支持。
很難找到一種方法在所有情況下都取到頂層對象,因此ES2020引入了globalThis作為頂層對象。
關(guān)于是否存在變量提升的爭議問題
在學(xué)習(xí)該部分知識時,依據(jù)的是ES6入門教程以及MDN中文文檔的內(nèi)容,沒有過多參看其他官方文檔,便斷定let和const不存在變量提升。但經(jīng)過大佬指點后拜讀了mdn英文文檔,文檔中描述了對let、const和class來說是存在變量提升的,以下做詳細解釋。
MDN英文文檔說明存在以下三種認為是變量提升(Hositing)的行為:
【原文】Hoisting is not a term normatively defined in the ECMAScript specification. The spec does define a group of declarations as?HoistableDeclaration, but this only includes?function,?function*,?async function, and?async function*?declarations. Hoisting is often considered a feature of?var?declarations as well, although in a different way. In colloquial terms, any of the following behaviors may be regarded as hoisting:
【翻譯】Hosting(變量提升)不是 ECMAScript 規(guī)范中規(guī)范定義的術(shù)語。該規(guī)范確實將一組聲明定義為HoistableDeclaration,但這僅包括函數(shù),函數(shù)*,異步函數(shù)和異步函數(shù)*聲明。變量提升通常也被認為是var聲明的一個特征,盡管方式不同。通俗地說,有下列行為之一,可視為變量提升:
【翻譯】能夠在聲明變量的行之前在其范圍內(nèi)使用變量的值。(“值變量提升”)
【實例】ECMAScript規(guī)范說明中寫到作為HoistableDeclaration的四種function聲明:function,?function*,?async function和async function*
【翻譯】能夠在聲明該變量的行之前在其作用域中引用該變量,而不會引發(fā)ReferenceError,但該值始終未定義。(“聲明變量提升”)
【實例】var命令的變量提升
【翻譯】變量的聲明會導(dǎo)致在聲明它的行之前在其作用域中發(fā)生行為更改。
【實例】let、const和class聲明命令(也統(tǒng)稱為詞匯聲明lexical declarations)
看到這里有個問題:既然存在變量提升,為何ES6入門和MDN中文文檔中都沒有注明呢?
MDN英文文檔也給出了解釋:
因為暫時性死區(qū)的存在,嚴格禁止了在變量聲明前進行使用,所以很多地方都認為let、const和class聲明命令不存在變量提升。這種異議也合理的,因為變量提升并不是一個被定義到ECMAScript中的普遍認可的術(shù)語(universally-agreed term)。但是暫時性死區(qū)可能會導(dǎo)致其范圍內(nèi)發(fā)生其他可觀察到的變化,這表明是存在著變量提升的。
實例如下:
【原文】If the?const x = 2?declaration is not hoisted at all (as in, it only comes into effect when it's executed), then the?console.log(x)?statement should be able to read the x value from the upper scope. However, because the?const?declaration still "taints" the entire scope it's defined in, the?console.log(x)?statement reads the?x?from the?const x = 2?declaration instead, which is not yet initialized, and throws a?ReferenceError. Still, it may be more useful to characterize lexical declarations as non-hoisting, because from a utilitarian perspective, the hoisting of these declarations doesn't bring any meaningful features.
【翻譯】如果說?const x = 2的聲明完全沒有被變量提升(那么它只在執(zhí)行時生效),那么console.log(x)應(yīng)該是能夠從上層作用域中讀取到x的值的。但此處因為const的聲明依然“污染”到了它定義的整個作用域,所以console.log(x)語句實際上讀取到的是const x = 2?聲明的x,并拋出ReferenceError。盡管如此,將詞匯聲明(lexical declarations)?描述為非變量提升可能更有用,因為從功利的角度來看,這些變量提升不會帶來任何有意義的特征。
【注意】以下這種情況不屬于變量提升:
{var x = 1; } console.log(x); // 1 復(fù)制代碼因為這里沒有“先訪問后聲明”,這里只是因為var聲明沒有在塊范圍內(nèi)。
結(jié)論
綜上所述,實際上let、const是存在變量提升的,但通常不會刻意去這樣描述,因為從功利的角度來看,這些變量提升并不會帶來任何有意義的特征。
??給大家推薦一個實用面試題庫
1、前端面試題庫 (面試必備) ? ? ? ? ? ?推薦:★★★★★
地址:前端面試題庫
總結(jié)
以上是生活随笔為你收集整理的【面试题】【ES6】let和const命令 (面试必看)的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: N 个Android 视频教程免费下载
- 下一篇: 大家都在找那个过滤呜呜祖拉的软件