面試官常問(wèn)之說(shuō)說(shuō)js中var、let、const的區(qū)別
前言
關(guān)于 var、let 和 const 三個(gè)關(guān)鍵字的區(qū)別,是一個(gè)老生常談的問(wèn)題,也是經(jīng)典的面試題。本篇文章將全面講解三者的特性,以及它們之間的區(qū)別,由淺入深讓你徹底搞懂這個(gè)知識(shí)點(diǎn)。
變量聲明
ECMAScript 變量是松散類型的,意思就是變量可以用于保存任何類型的數(shù)據(jù),每個(gè)變量只不過(guò)是一個(gè)用于保存任意值的命名占位符。
有3個(gè)關(guān)鍵字可以聲明變量:var、let和const,var在 ECMAScript 的所有版本中都可以使用,而let和const只能在 ES6 及更晚的版本中使用。
var
要定義變量,可以使用var操作符(注意 var 是一個(gè)關(guān)鍵字),后跟變量名(即標(biāo)識(shí)符,如前所述):
var message;
上面這行代碼定義了一個(gè)名為message的變量,它可以保存任何類型的值。不初始化的情況下,變量會(huì)保存一個(gè)特殊值undefined。
ECMAScript 實(shí)現(xiàn)變量初始化,因此可以同時(shí)定義變量并設(shè)置它的值:
var messgae = "hi";
message被定義為一個(gè)保存字符串值 "hi" 的變量。像這樣初始化變量不會(huì)將它標(biāo)識(shí)為字符串類型,只是一個(gè)簡(jiǎn)單的賦值而已。隨后,不僅可以改變保存的值,還可以改變值的類型:
var message = "hi"; message = 666; // 合法,但不推薦 console.log(message); // 666
如果需要定義多個(gè)變量,可以在一條語(yǔ)句中用逗號(hào)分隔每個(gè)變量(及可選的初始化):
var message, name = "孫悟空", age = 18;
作用域
使用var操作符定義的變量會(huì)成為包含它的函數(shù)的局部變量。例如,使用var在一個(gè)函數(shù)內(nèi)部定義一個(gè)變量,就意味著該變量將在函數(shù)退出時(shí)被銷毀:
function test() { var message = "hi"; // 局部變量 } test(); console.log(message); // error: message is not defined
不過(guò),在函數(shù)內(nèi)部定義變量時(shí)省略var操作符,可以創(chuàng)建一個(gè)全局變量:
function test() { message = "hi"; // 全局變量 } test(); console.log(message); // ok
雖然可以通過(guò)省略var操作符定義全局變量,但不推薦這么做。在嚴(yán)格模式下,如果像這樣給未聲明的變量賦值,會(huì)報(bào)錯(cuò)。
在同一個(gè)作用域內(nèi),反復(fù)多次使用var聲明同一個(gè)變量也是沒問(wèn)題的:
function test() { var message = "hi"; var message = false; var message = 666; console.log(message); } test(); // 666
提升
所謂的提升,就是把所有變量聲明都拉到函數(shù)作用域的頂部。注意,只提升聲明,不提升賦值操作。
舉個(gè)栗子:
function test() { console.log(message); var message = "hi"; } test(); // undefined
上面的代碼不會(huì)報(bào)錯(cuò),因?yàn)槭褂胿ar關(guān)鍵字聲明的變量會(huì)提升到函數(shù)作用域的頂部,跟下面的代碼是等價(jià)的:
function test() { var message; console.log(message); message = "hi"; } test(); // undefined
let
let 和 var 的作用差不多,但有著非常重要的區(qū)別。
作用域
var聲明的范圍是函數(shù)作用域,而let聲明的范圍是塊作用域:
if (true) { var message = "hi"; console.log(message); // hi } console.log(message); // hi if (true) { let message = "hi"; console.log(message); // hi } console.log(message); // error: message is not defined
message變量之所以不能在 if 塊外部被引用,是因?yàn)樗淖饔糜騼H限于該塊內(nèi)部。塊作用域是函數(shù)作用域的子集,因此適用于var的作用域限制同樣也適用于let。
let 不允許同一個(gè)塊作用域中出現(xiàn)冗余聲明:
if (true) { // error: 無(wú)法重新聲明塊范圍變量“a” let a; let a; }
JS 引擎會(huì)記錄用于變量聲明的標(biāo)識(shí)符及其所在的塊作用域,因此嵌套使用相同的標(biāo)識(shí)符不會(huì)報(bào)錯(cuò),這是因?yàn)橥粋€(gè)塊中沒有重復(fù)聲明:
let a = 666; console.log(a); // 666 if (true) { let a = '啊哈哈'; console.log(a); // 啊哈哈 }
var和let聲明的并不是不同類型的變量,它們只是指出變量在相關(guān)作用域如何存在,所以對(duì)聲明冗余報(bào)錯(cuò)不會(huì)因混用var和let而受影響:
// error var a; let a; // error let b; var b;
暫時(shí)性死區(qū)
let聲明的變量不會(huì)在作用域中被提升:
if (true) { console.log(x); // error: 在賦值前使用了變量“x” let x = 520; }
在解析代碼時(shí),JS 引擎也會(huì)注意出現(xiàn)在塊后面的let聲明,只不過(guò)在此之前不能以任何方式來(lái)引用未聲明的變量。在let聲明之前的執(zhí)行瞬間被稱為暫時(shí)性死區(qū),在此階段引用任何后面才聲明的變量都會(huì)報(bào)錯(cuò)。
const
const的行為與let基本相同,唯一一個(gè)重要的區(qū)別是:用const聲明變量時(shí)必須同時(shí)初始化變量,且嘗試修改const聲明的變量會(huì)導(dǎo)致運(yùn)行時(shí)報(bào)錯(cuò):
const a; // error: 必須初始化 "const" 聲明 const b = 250; b = 520; // error: 無(wú)法賦值給 "b" ,因?yàn)樗浅?shù)
注意,const聲明的限制只適用于它指向的變量的引用,換句話說(shuō),如果const變量引用的是一個(gè)對(duì)象,那么修改這個(gè)對(duì)象內(nèi)部的屬性并不違反const限制:
const obj = { x: 666 }; obj.x = 888; // ok obj.y = '啊哈哈'; // ok
擴(kuò)展
全局聲明
使用var在全局作用域中聲明的變量會(huì)成為window對(duì)象的屬性,let和const聲明的變量則不會(huì):
var a = 666; console.log(window.a); // 666 let b = 666; console.log(window.b); // undefined const c = 666; console.log(window.c); // undefined
for 循環(huán)中的 let 聲明
先看個(gè)??:
for (var i = 0; i < 5; i++) { ... } console.log(i);
思考一下,打印結(jié)果會(huì)是什么?
由于var聲明的變量沒有塊作用域,所以迭代變量i會(huì)滲透到循環(huán)體外部,當(dāng)i遞增到5時(shí)退出循環(huán),打印出結(jié)果為5。
如果用let聲明迭代變量,就可以把迭代變量的作用域限制為 for 循環(huán)塊內(nèi)部:
for (let i = 0; i < 5; i++) { ... } console.log(i); // error: i is not defined
再看個(gè)栗子:
for (var i = 0; i < 5; i++) { setTimeout( () => { console.log(i); }, 0 ) }
你可能以為會(huì)輸出:0、1、2、3、4,實(shí)際上會(huì)輸出:5、5、5、5、5。
之所以是這樣的結(jié)果,是因?yàn)樵谕顺鲅h(huán)時(shí),迭代變量保存的是導(dǎo)致循環(huán)退出的值,也就是 5。在之后異步執(zhí)行超時(shí)邏輯時(shí),所有的i都是同一個(gè)變量,因此輸出的都是同一個(gè)最終值。
而在使用let聲明迭代變量時(shí),JS 引擎在后臺(tái)會(huì)為每個(gè)迭代循環(huán)聲明一個(gè)新的迭代變量,每個(gè) setTimeout 引用的都是不同的變量實(shí)例,所以 console.log 輸出的是我們期望的值,也就是循環(huán)執(zhí)行過(guò)程中每個(gè)迭代變量的值:
for (let i = 0; i < 5; i++) { setTimeout( () => { console.log(i); // 0、1、2、3、4 }, 0 ) }
這是一道經(jīng)典面試題,不是很理解的話一定要多看幾遍,最好動(dòng)手實(shí)踐一下,徹底搞懂為止。
總結(jié)
var 聲明的范圍是函數(shù)作用域,let 和 const 聲明的范圍是塊作用域
var 聲明的變量會(huì)被提升到函數(shù)作用域的頂部,let 和 const 聲明的變量不存在提升,且具有暫時(shí)性死區(qū)特征
var 允許在同一個(gè)作用域中重復(fù)聲明同一個(gè)變量,let 和 const 不允許
在全局作用域中使用 var 聲明的變量會(huì)成為 window 對(duì)象的屬性,let 和 const 聲明的變量則不會(huì)
const 的行為與 let 基本相同,唯一一個(gè)重要的區(qū)別是,使用 const 聲明的變量必須進(jìn)行初始化,且不能被修改
聲明風(fēng)格及最佳實(shí)踐
ECMAScript 6 增加 let 和 const 從客觀上為這門語(yǔ)言更精確地聲明作用域和語(yǔ)義提供了更好的支持,行為怪異的 var 所造成的問(wèn)題,已經(jīng)讓 JS 社區(qū)為之苦惱了很多年。隨著這兩個(gè)關(guān)鍵字的出現(xiàn),新的有助于提升代碼質(zhì)量的最佳實(shí)踐方式也逐漸顯現(xiàn)。
var 已經(jīng)被時(shí)代所拋棄,不建議再使用。限制自己只使用 let 和 const 有助于提升代碼質(zhì)量,因?yàn)樽兞坑辛嗣鞔_的作用域、聲明位置以及不變的值。
優(yōu)先使用 const,let 次之。使用 const 聲明可以讓瀏覽器運(yùn)行時(shí)強(qiáng)制保持變量不變,也可以讓靜態(tài)代碼分析工具提前發(fā)現(xiàn)不合法的賦值操作。只在提前知道未來(lái)會(huì)有修改時(shí),才使用 let。這樣可以讓開發(fā)者更有信心地推斷某些變量的值永遠(yuǎn)不會(huì)變,同時(shí)也能迅速發(fā)現(xiàn)因意外賦值導(dǎo)致的非預(yù)期行為。
到此這篇關(guān)于ja中var、let、const區(qū)別的文章就介紹到這了,更多相關(guān)var、let、const區(qū)別內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
JS+HTML實(shí)現(xiàn)自定義上傳圖片按鈕并顯示圖片功能的方法分析
這篇文章主要介紹了JS+HTML實(shí)現(xiàn)自定義上傳圖片按鈕并顯示圖片功能的方法,結(jié)合實(shí)例形式分析了JavaScript圖片上傳、編碼轉(zhuǎn)換等相關(guān)操作技巧,需要的朋友可以參考下2020-02-02完美實(shí)現(xiàn)八種js焦點(diǎn)輪播圖(上篇)
這篇文章主要介紹了完美實(shí)現(xiàn)八種js焦點(diǎn)輪播圖的具體資料,基于完美運(yùn)動(dòng)框架move2.js而完成的八種焦點(diǎn)錄播圖,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2016-07-07jQuery ajax(復(fù)習(xí))—Baidu ajax request分離版
你沒有看錯(cuò)標(biāo)題,本文的確是在講Baidu ajax,不過(guò)是很久很久以前的版本了,我們先分析一段簡(jiǎn)單的ajax代碼,來(lái)自早期的百度七巧板項(xiàng)目通過(guò)這個(gè)來(lái)先復(fù)習(xí)一遍ajax的知識(shí)2013-01-01原生JavaScript實(shí)現(xiàn)購(gòu)物車效果
這篇文章主要為大家詳細(xì)介紹了原生JavaScript實(shí)現(xiàn)購(gòu)物車效果,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2022-07-07JS實(shí)現(xiàn)的N多簡(jiǎn)單無(wú)縫滾動(dòng)代碼(包含圖文效果)
這篇文章主要介紹了JS實(shí)現(xiàn)的N多簡(jiǎn)單無(wú)縫滾動(dòng)代碼,包含了文字及圖文等多種滾動(dòng)效果,涉及JavaScript遞歸調(diào)用及定時(shí)函數(shù)觸發(fā)實(shí)現(xiàn)頁(yè)面元素動(dòng)態(tài)變換的相關(guān)技巧,需要的朋友可以參考下2015-11-11Jquery和JS用外部變量獲取Ajax返回的參數(shù)值的方法實(shí)例(超簡(jiǎn)單)
Jquery和JS用外部變量獲取Ajax返回的參數(shù)值的方法實(shí)例(超簡(jiǎn)單),需要的朋友可以參考一下2013-06-06web項(xiàng)目開發(fā)之JS函數(shù)防抖與節(jié)流示例代碼
這篇文章主要介紹了web項(xiàng)目開發(fā)之JS函數(shù)防抖與節(jié)流實(shí)現(xiàn)的示例代碼及原理解析,有需要的朋友可以借鑒參考下,希望能夠有所幫助2021-09-09