JavaScript三大變量聲明詳析
前言
ECMAScript 變量是松散類型的,變量可以用于保存任何類型的數(shù)據(jù),每個(gè)變量只不過(guò)是一個(gè)用于保存任意值的命名占位符。
本文主要講述 Javascript 中三大變量聲明的用法與寫法,有 3 個(gè)關(guān)鍵字可以聲明變量:var
、const
和 let
。其中,var
在ECMAScript
的所有版本中都可以使用,而 const
和 let
只能在 ECMAScript 6
及更晚的版本中使用。(現(xiàn)在最新版為 ECMAScript 12
)
Var
var 操作符用于定義變量(var 也是一個(gè)關(guān)鍵字),后跟變量名(即標(biāo)識(shí)符)
基礎(chǔ)寫法
聲明未定義值
這里定義了一個(gè)名為test的變量,可以用它保存任何類型的值;
?var test;
注意: 不初始化的情況下,變量會(huì)保存一個(gè)特殊值
undefined
聲明定義值
ECMAScript
實(shí)現(xiàn)變量初始化,因此可以同時(shí)定義變量并設(shè)置它的值,如下:
?// 聲明定義值 ?var test = "good job";
像這樣的初始化變量不會(huì)將它標(biāo)識(shí)為字符串類型,隨時(shí)可以改變保存的值,從而改變值的類型。
不推薦寫法
在下面這個(gè)例子中,變量 test
首先被定義為一個(gè)保存字符串值的變量,然后又被重寫為保存了數(shù)值:
?var test = "good job"; ? ?test = 299; ?//合法,但不推薦 ?/* 像這種單獨(dú)的變量可以直接賦值就沒(méi)必要另起去改了 沒(méi)必要 */
雖然不推薦改變變量保存值的類型,但這在 ECMAScript
中是完全有效的。
var 聲明作用域
使用 var
定義的變量會(huì)成為包含它的函數(shù)的局部變量,簡(jiǎn)稱為作用域變量。
局部作用域
使用 var
在一個(gè)函數(shù)內(nèi)部定義一個(gè)變量,就意味著該變量將在函數(shù)退出時(shí)被銷毀,如下:
?function fun() { ? var test = "Hello World"; // 局部變量 ?} ?fun(); ?console.log(test); // 出錯(cuò)!
這里,test
變量是在函數(shù)內(nèi)部使用 var 定義的,函數(shù)為 fun()
,調(diào)用它會(huì)創(chuàng)建這個(gè)變量并給它賦值;調(diào)用之后變量隨即被銷毀,因此示例中的最后一行會(huì)導(dǎo)致錯(cuò)誤。
全局作用域
在函數(shù)內(nèi)定義變量時(shí)省略 var 操作符,可以創(chuàng)建一個(gè)全局變量,如下
?function fun() { ? test = "Hello World"; // 局部變量 ?} ?fun(); ?console.log(test); // "Hello World"
去掉之前的 var 操作符之后,test
就變成了全局變量;只要調(diào)用一次函數(shù) fun()
,就會(huì)定義這個(gè)變量,并且可以在函數(shù)外部訪問(wèn)到。
注意:雖然可以通過(guò)省略
var
操作符定義全局變量,但不推薦這么做。在嚴(yán)格模式下,如果像這樣給未聲明的變量賦值,則會(huì)導(dǎo)致拋出 ReferenceError。
便捷語(yǔ)法
如果需要定義多個(gè)變量,可以在一條語(yǔ)句中用逗號(hào)分隔每個(gè)變量(及可選的初始化),如下:
?var name = "hey~~", ? name = "shrimpsss", ? age = 99;
這里定義并初始化了 3 個(gè)變量,因?yàn)?nbsp;ECMAScript
是松散類型的,所以使用不同數(shù)據(jù)類型初始化的變量可以用一條語(yǔ)句來(lái)聲明; 雖然插入換行和空格縮進(jìn)并不是必需的,但這樣有利于閱讀理解。
注意: 在嚴(yán)格模式下,不能定義名為
eval
和arguments
的變量,否則會(huì)導(dǎo)致語(yǔ)法錯(cuò)誤。
var 聲明提升
var 這個(gè)關(guān)鍵字在函數(shù)中聲明變量時(shí)會(huì)自動(dòng)提升到函數(shù)作用域頂部,如下:
?function foo() { ? console.log(age); ? var age = 26; ?} ?foo(); // undefined
之所以不會(huì)報(bào)錯(cuò),是因?yàn)?nbsp;ECMAScript
運(yùn)行時(shí)把它看成等價(jià)于如下代碼:
?function foo() { ? var age; ? console.log(age); ? age = 26; ?} ?foo(); // undefined
這就所謂的“提升”(hoist),也就是把所有變量聲明都拉到函數(shù)作用域的頂部。
Let
let
跟 var
的作用差不多,但 let
聲明的范圍是塊作用域(var
聲明的范圍是函數(shù)作用域)
塊作用域是函數(shù)作用域的子集,因此適用于 var
的作用域限制同樣也適用于 let
let 作用域
let 作用域只能在塊作用域里訪問(wèn),不能跨塊訪問(wèn),也不能跨函數(shù)訪問(wèn)
兩者的對(duì)比會(huì)更加凸顯不一,如下:
?// --- var --- ?if (true) { ? var name = 'king'; ? console.log(name); // Matt ?} ?console.log(name); // Matt ?? ?// --- let --- ?if (true) { ? let age = 26; ? console.log(age); // 26 ?} ?console.log(age); // ReferenceError: age 沒(méi)有定義
在這里,age 變量之所以不能在 if 塊外部被引用,是因?yàn)樗淖饔糜騼H限于該塊內(nèi)部。
冗余聲明
let 不允許同一個(gè)塊作用域中出現(xiàn)冗余聲明
?var name; ?var name; ?let age; ?let age; // SyntaxError;標(biāo)識(shí)符 age 已經(jīng)聲明過(guò)了
冗余聲明的報(bào)錯(cuò)不會(huì)因混用 let
和 var
而受影響。
這兩個(gè)關(guān)鍵字聲明的并不是不同類型的變量,它們只是指出變量在相關(guān)作用域如何存在,如下:
?var name; ?let name; // SyntaxError ?let age; ?var age; // SyntaxError
JavaScript 引擎會(huì)記錄用于變量聲明的標(biāo)識(shí)符及其所在的塊作用域,因此嵌套使用相同的標(biāo)識(shí)符不會(huì)報(bào)錯(cuò)。
暫時(shí)性死區(qū)
let
與 var
的另一個(gè)重要的區(qū)別,就是 let
聲明的變量不會(huì)在作用域中被提升
?// name 會(huì)被提升 ?console.log(name); // undefined ?var name = 'vito'; ?? ?// age 不會(huì)被提升 ?console.log(age); // ReferenceError:age 沒(méi)有定義 ?let age = 26
在解析代碼時(shí),JavaScript 引擎也會(huì)注意出現(xiàn)在塊后面的 let
聲明,只不過(guò)在此之前不能以任何方式來(lái)引用未聲明的變量。
在 let 聲明之前的執(zhí)行瞬間被稱為“暫時(shí)性死區(qū)”(temporal dead zone),在此階段引用任何后面才聲明的變量都會(huì)拋出
ReferenceError
。
全局聲明
與 var
關(guān)鍵字不同,使用 let
在全局作用域中聲明的變量不會(huì)成為 window
對(duì)象的屬性
?/* window.xx:在window對(duì)象中查找此屬性(元素)*/ ?var name = 'vito'; ?console.log(window.name); // 'vito' ?? ?let age = 26; ?console.log(window.age); // undefined
但 let
聲明仍然是在全局作用域中發(fā)生的,所以相應(yīng)變量會(huì)在頁(yè)面的生命周期內(nèi)存續(xù)。
條件聲明
因?yàn)?let 的作用域是塊,所以不可能檢查前面是否已經(jīng)使用 let 聲明過(guò)同名變量,同時(shí)也就不可能在沒(méi)有聲明的情況下聲明它,沒(méi)有與var
一樣的聲明提升。
?/* 方便理解: 把兩個(gè)<script>代碼塊看做一個(gè)代碼塊 */ ?? ?<script> ? var name = 'Nicholas'; ? let age = 26; ?</script> ?? ?<script> ? // 假設(shè)腳本不確定頁(yè)面中是否已經(jīng)聲明了同名變量 ? // 那它可以假設(shè)還沒(méi)有聲明過(guò) ? var name = 'Matt'; ? // 這里沒(méi)問(wèn)題,因?yàn)榭梢员蛔鳛橐粋€(gè)提升聲明來(lái)處理 ? // 不需要檢查之前是否聲明過(guò)同名變量 ? let age = 36; ? // 如果 age 之前聲明過(guò),這里會(huì)報(bào)錯(cuò) ?</script>
為此, let
這個(gè)新的 ES6
聲明關(guān)鍵字,不能依賴條件聲明模式
注意:
let
不能進(jìn)行條件式聲明是件好事,因?yàn)闂l件聲明是一種反模式,它讓程序變得更難理解
for 循環(huán)中的 let
在 let 出現(xiàn)之前,for 循環(huán)定義的迭代變量會(huì)滲透到循環(huán)體外部。
常見(jiàn)for循環(huán)
如下,用 var 示例:
?for (var i = 0; i < 5; ++i) { ? // 循環(huán)邏輯 ?} ?console.log(i); // 5
改成使用 let
就不會(huì)有這個(gè)問(wèn)題了,因?yàn)?nbsp;let
迭代變量的作用域僅限于 for 循環(huán)塊內(nèi)部:
?for (let i = 0; i < 5; ++i) { ? // 循環(huán)邏輯 ?} ?console.log(i); // ReferenceError: i 沒(méi)有定義 復(fù)制代碼
for循環(huán)套定時(shí)器
在使用 var 的時(shí)候,最常見(jiàn)的問(wèn)題就是對(duì)迭代變量的奇特聲明和修改,如下:
?for (var i = 0; i < 5; ++i) { ? setTimeout(() => console.log(i), 0) ?} ?// 實(shí)際上會(huì)輸出 5、5、5、5、5
怎么樣,是不是以為會(huì)輸入 0、1、2、3、4 ?
之所以會(huì)這樣,是因?yàn)樵谕顺鲅h(huán)時(shí),迭代變量保存的是導(dǎo)致循環(huán)退出的值:5。在之后執(zhí)行超時(shí)邏輯時(shí),所有的 i 都是同一個(gè)變量,因而輸出的都是同一個(gè)最終值。
而在使用 let 聲明迭代變量時(shí),JavaScript 引擎在后臺(tái)會(huì)為每個(gè)迭代循環(huán)聲明一個(gè)新的迭代變量。
?for (let i = 0; i < 5; ++i) { ? setTimeout(() => console.log(i), 0) ?} ?// 會(huì)輸出 0、1、2、3、4
每個(gè) setTimeout
引用的都是不同的變量實(shí)例,所以 console.log
輸出的是我們期望的值,也就是循環(huán)執(zhí)行過(guò)程中每個(gè)迭代變量的值。
這種每次迭代聲明一個(gè)獨(dú)立變量實(shí)例的行為適用于所有風(fēng)格的
for
循環(huán),包括for-in
和for-of
循環(huán)。
const
const跟 let 的行為差不多,但區(qū)別是用它聲明變量時(shí)必須同時(shí)初始化變量,且嘗試修改 const 聲明的變量會(huì)導(dǎo)致運(yùn)行時(shí)錯(cuò)誤(一致性)
const 限制
- const 不可改值
?const age = 26; ?age = 36; // TypeError
- const 也不允許重復(fù)聲明
?const name = 'Lisa'; ?const name = 'Vito'; // SyntaxError
- const 聲明的作用域也是塊
?const name = 'apple'; ?if (true) { ? const name = 'avocado'; ?} ?console.log(name); // apple
const
聲明的限制只適用于它指向的變量的引用。 換句話說(shuō),如果 const
變量引用的是一個(gè)對(duì)象,那么修改這個(gè)對(duì)象內(nèi)部的屬性并不違反 const
的限制,如下:
?const person = {}; ?person.name = 'vito'; // ok
怎么樣,是不是很神奇 o( ̄▽ ̄)d
for 循環(huán)中的 const
JavaScript 引擎會(huì)為 for
循環(huán)中的 let
聲明分別創(chuàng)建獨(dú)立的變量實(shí)例,雖然 const
變量跟 let
變量很相似,但是不能用 const
來(lái)聲明迭代變量(因?yàn)榈兞繒?huì)自增):
?for (const i = 0; i < 10; ++i) {} ?// TypeError 類型錯(cuò)誤
不過(guò),如果你只想用 const
聲明一個(gè)不會(huì)被修改的 for
循環(huán)變量,那也是可以的。
每次迭代只是創(chuàng)建一個(gè)新變量,這對(duì) for-of
和 for-in
循環(huán)特別有意義的,如下:
?let i = 0; ?for (const j = 7; i < 5; ++i) { ? console.log(j); ?} ?// 7, 7, 7, 7, 7 ?? ?for (const key in {a: 1, b: 2}) { ? console.log(key); ?} ?// a, b ?? ?for (const value of [1,2,3,4,5]) { ? console.log(value); ?} ?// 1, 2, 3, 4, 5
聲明風(fēng)格及最佳實(shí)踐
ECMAScript 6
增加 let
和 const
為這門語(yǔ)言更精確地聲明作用域和語(yǔ)義提供了更好的支持
- 不使用
var
有了 let 和 const,大多數(shù)開(kāi)發(fā)者會(huì)發(fā)現(xiàn)自己不再需要 var 了。限制自己只使用 let
和 const
有助于提升代碼質(zhì)量,因?yàn)樽兞坑辛嗣鞔_的作用域、聲明位置,以及不變的值。
const
優(yōu)先,let
次之
使用 const
聲明可以讓瀏覽器運(yùn)行時(shí)強(qiáng)制保持變量不變,也可以讓靜態(tài)代碼分析工具提前發(fā)現(xiàn)不合法的賦值操作。因此,應(yīng)該優(yōu)先使用 const
來(lái)聲明變量,只在提前知道未來(lái)會(huì)有修改時(shí),再使用 let。
總結(jié)
ECMAScript
變量是松散類型的,變量可以用于保存任何類型的數(shù)據(jù)var
定義的變量,沒(méi)有塊的概念,可以跨塊訪問(wèn), 不能跨函數(shù)訪問(wèn)。let
定義的變量,只能在塊作用域里訪問(wèn),不能跨塊訪問(wèn),也不能跨函數(shù)訪問(wèn)。const
用來(lái)定義常量,使用時(shí)必須初始化(即必須賦值),只能在塊作用域里訪問(wèn),而且不能修改。- 對(duì)于
let
與const
同一個(gè)變量只能使用一種方式聲明,不然會(huì)報(bào)錯(cuò) - 定義變量?jī)?yōu)先使用
const
,次之let
,養(yǎng)成良好代碼規(guī)范
到此這篇關(guān)于JavaScript三大變量聲明詳析的文章就介紹到這了,更多相關(guān)JS變量聲明內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
深入理解JavaScript系列(14) 作用域鏈介紹(Scope Chain)
在第12章關(guān)于變量對(duì)象的描述中,我們已經(jīng)知道一個(gè)執(zhí)行上下文 的數(shù)據(jù)(變量、函數(shù)聲明和函數(shù)的形參)作為屬性存儲(chǔ)在變量對(duì)象中2012-04-04Three.js利用性能插件stats實(shí)現(xiàn)性能監(jiān)聽(tīng)的方法
Three.js 是一款運(yùn)行在瀏覽器中的 3D 引擎,你可以用它創(chuàng)建各種三維場(chǎng)景,而下面這篇文章主要給大家介紹了關(guān)于Three.js如何利用性能插件stats實(shí)現(xiàn)性能監(jiān)聽(tīng)的相關(guān)資料,需要的朋友可以參考借鑒,下面來(lái)一起學(xué)習(xí)學(xué)習(xí)吧。2017-09-09JavaScript實(shí)現(xiàn)清空(重置)文件類型INPUT元素值的方法
這篇文章主要介紹了JavaScript實(shí)現(xiàn)清空(重置)文件類型INPUT元素值的方法,結(jié)合實(shí)例形式分析了javascript清空input文本框的常用方法與實(shí)現(xiàn)技巧,需要的朋友可以參考下2016-11-11微信小程序?qū)崿F(xiàn)頁(yè)面左右滑動(dòng)
這篇文章主要為大家詳細(xì)介紹了微信小程序?qū)崿F(xiàn)頁(yè)面左右滑動(dòng),文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2020-11-11mpvue 頁(yè)面預(yù)加載新增preLoad生命周期的兩種方式
這篇文章主要介紹了mpvue 頁(yè)面預(yù)加載新增preLoad生命周期的兩種方式,本文重點(diǎn)給大家講解了第一種方式,需要的朋友可以參考下2019-10-10基于MVC方式實(shí)現(xiàn)三級(jí)聯(lián)動(dòng)(JavaScript)
這篇文章主要為大家詳細(xì)介紹了基于MVC方式實(shí)現(xiàn)三級(jí)聯(lián)動(dòng)的相關(guān)資料,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2017-01-01基于小程序請(qǐng)求接口wx.request封裝的類axios請(qǐng)求
這篇文章主要介紹了基于小程序請(qǐng)求接口wx.request封裝的類axios請(qǐng)求,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2020-07-07JS表單數(shù)據(jù)驗(yàn)證的正則表達(dá)式(常用)
這篇文章主要介紹了JS表單數(shù)據(jù)驗(yàn)證的正則表達(dá)式,這種方法比較常用,以及使用正則表達(dá)式驗(yàn)證表單的方法,本文給大家介紹非常詳細(xì),需要的的朋友參考下2017-02-02ie下$.getJSON出現(xiàn)問(wèn)題的解決方法
ie下$.getJSON出現(xiàn)問(wèn)題是常有的事,下面為大家介紹下具體該如何解決,需要的朋友可以參考下2014-02-02JavaScript變速動(dòng)畫函數(shù)封裝添加任意多個(gè)屬性
這篇文章主要介紹了JavaScript變速動(dòng)畫函數(shù)封裝添加任意多個(gè)屬性 ,非常不錯(cuò),具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2019-04-04