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