JavaScript 中的變量聲明與賦值
在計(jì)算機(jī)編程中,使用名稱(或標(biāo)識符)來表示值是最基本的技術(shù)之一。將名稱與值綁定為我們提供了一種在程序中引用值并利用它們的方式。當(dāng)涉及到綁定名稱與值時(shí),我們通常稱之為將值賦給變量。術(shù)語“變量”暗示了新的值可以被賦給它,這意味著與變量關(guān)聯(lián)的值在程序執(zhí)行過程中可能會(huì)改變。如果一個(gè)值被永久地分配給一個(gè)名稱,那么該名稱可以被視為常量而不是變量。
在 JavaScript 中使用變量或常量之前,必須先進(jìn)行聲明。在 ES6 版本及更高版本的 JavaScript 中,可以使用 let
和 const
關(guān)鍵字來實(shí)現(xiàn)這一點(diǎn),我們將在稍后介紹。在 ES6 之前,變量使用 var
關(guān)鍵字進(jìn)行聲明。
1. 使用 let
和 const
進(jìn)行聲明。
在現(xiàn)代 JavaScript(ES6 及更高版本)中,變量使用 let
關(guān)鍵字進(jìn)行聲明:
也可以在單個(gè) let
語句中聲明多個(gè)變量:
在聲明時(shí),最好將初始值賦給變量(如果可能的話):
如果在 let
語句中沒有提供初始值,變量仍將被聲明,但其值將是 undefined
,直到它被賦值。要聲明常量而不是變量,請使用 const
關(guān)鍵字而不是 let
。const
類似于 let
,但關(guān)鍵區(qū)別在于常量在聲明時(shí)必須進(jìn)行初始化:
正如名稱所示,常量的值不能被更改,嘗試重新分配常量會(huì)導(dǎo)致 TypeError
。聲明常量的通用(盡管不是普遍的)約定是使用大寫字母,例如 H0
或 HTTP_NOT_FOUND
,以將它們與變量區(qū)分開。
何時(shí)使用 const
。
在使用 const
關(guān)鍵字方面存在兩種觀點(diǎn)。一個(gè)觀點(diǎn)是僅在預(yù)期值不會(huì)更改的情況下使用 const
,例如物理常數(shù)、程序版本號或用于識別文件類型的字節(jié)順序標(biāo)記。另一個(gè)觀點(diǎn)認(rèn)為,在程序中,許多所謂的變量實(shí)際上在運(yùn)行時(shí)并不會(huì)改變。因此,建議是使用 const
來聲明所有變量,僅在發(fā)現(xiàn)需要允許它們的值發(fā)生變化時(shí)將其切換為 let
。這種方法有助于避免意外的變量修改,從而導(dǎo)致錯(cuò)誤。
在第一種情況下,const
僅用于必須保持恒定的值。在第二種情況下,const
用于任何不會(huì)更改的值。就個(gè)人而言,在編寫代碼時(shí),我更傾向于前一種方法。
JavaScript 中的循環(huán)結(jié)構(gòu):for
、for/in
和 for/of
循環(huán)。
在 JavaScript 中,循環(huán)結(jié)構(gòu)(如 for
、for/in
和 for/of
循環(huán))包括一個(gè)在循環(huán)的每次迭代中獲取新值的循環(huán)變量。JavaScript 允許在循環(huán)語法中聲明這個(gè)循環(huán)變量,這是 let
的另一個(gè)常見用例:
雖然可能看起來有點(diǎn)不尋常,但也可以在 for/in
和 for/of
循環(huán)中使用 const
來聲明循環(huán)的“變量”,只要它們在循環(huán)體內(nèi)部不被重新賦值。在這種情況下,const
聲明表示循環(huán)變量的值在每次迭代中都是恒定的:
變量和常量的作用域。
變量的作用域是指在源代碼中定義變量的區(qū)域。使用 let
和 const
聲明的變量和常量具有塊級作用域。這意味著它們僅在 let
或 const
語句的代碼塊內(nèi)部定義。JavaScript 類和函數(shù)的函數(shù)體被視為代碼塊,同樣,if/else
語句的語句體和 while
、for
循環(huán)的循環(huán)體也被視為代碼塊。簡單來說,如果變量或常量聲明在一對花括號內(nèi)部,那么這些花括號限制了變量或常量定義的代碼區(qū)域(當(dāng)然,在引用這些變量或常量之前的代碼行中引用在 let
或 const
語句中聲明它們是無效的)。作為 for
、for/in
或 for/of
循環(huán)的一部分聲明的變量和常量的作用域僅限于循環(huán)體, 即使循環(huán)聲明位于花括號外部。
如果聲明發(fā)生在頂層,在任何代碼塊之外,它被稱為全局變量或常量,并具有全局作用域。在 Node 和客戶端 JavaScript 模塊中,全局變量的作用域由聲明它們的文件決定。但是,在傳統(tǒng)的客戶端 JavaScript 中,全局變量的作用域由聲明它們的 HTML 文檔決定。換句話說,如果 <script>
標(biāo)簽聲明了全局變量或常量,那么該變量或常量將在同一文檔中的任何 <script>
元素中定義(或者至少在所有在執(zhí)行 let
和 const
語句之后執(zhí)行的腳本中定義)。
重新聲明。
在同一作用域內(nèi)多次使用 let
或 const
語句聲明相同名稱是語法錯(cuò)誤。但是,在嵌套作用域中聲明相同名稱的變量是允許的(盡管通常建議避免這種情況):
聲明和數(shù)據(jù)類型。
如果您有使用靜態(tài)類型語言(例如 C 或 Java)的經(jīng)驗(yàn),可能會(huì)認(rèn)為變量聲明的主要目的是指定變量可以保存的數(shù)據(jù)類型。然而,我們還看到,在 JavaScript 中,變量聲明并不與值的數(shù)據(jù)類型相關(guān)聯(lián)。JavaScript 變量可以存儲(chǔ)任何類型的值。例如,在 JavaScript 中,將一個(gè)數(shù)字賦給變量,然后將一個(gè)字符串賦給同一個(gè)變量是有效的:
(程序員的軟技能:ke.qq.com/course/6034346)
2. 使用 var
進(jìn)行變量聲明。
在 ES6 之前的 JavaScript 中,聲明變量的唯一方式是使用 var
關(guān)鍵字,無法聲明常量。var
的語法與 let
類似:
雖然 var
和 let
具有相同的語法,但它們之間有重要的區(qū)別:
· 使用 var
聲明的變量沒有塊級作用域。這些變量的作用域限制在包含函數(shù)的函數(shù)體內(nèi),無論它們在函數(shù)內(nèi)部的嵌套有多深。
· 如果在函數(shù)體外部使用 var
,它將聲明一個(gè)全局變量。然而,使用 var
聲明的全局變量和使用 let
聲明的全局變量之間存在重要區(qū)別。使用 var
聲明的全局變量被實(shí)現(xiàn)為全局對象的屬性??梢允褂?globalThis
引用全局對象。因此,如果在函數(shù)外部編寫 var x = 2;
,它等同于編寫 globalThis.x = 2;
。然而,需要注意的是,這個(gè)類比并不完全準(zhǔn)確,因?yàn)橥ㄟ^全局 var
創(chuàng)建的屬性無法使用 delete
運(yùn)算符刪除。使用 let
和 const
聲明的全局變量和常量不是全局對象的屬性。
· 與使用 let
聲明的變量不同,使用 var
可以多次聲明具有相同名稱的變量。由于 var
變量具有函數(shù)作用域而不是塊級作用域,這種重新聲明非常常見。變量 i
經(jīng)常用于存儲(chǔ)整數(shù)值,特別是作為 for
循環(huán)中的索引變量。在包含多個(gè) for
循環(huán)的函數(shù)中,每個(gè)循環(huán)通常以 for (var i = 0; ...)
開始。由于 var
不限制這些變量的作用域僅限于循環(huán)體,每次迭代(無害地)重新聲明和重新初始化同一個(gè)變量。
· var
最顯著的特點(diǎn)之一是變量提升。使用 var
時(shí),聲明被提升到包含函數(shù)的頂部。然而,變量的初始化仍然發(fā)生在代碼中的原始位置;只是變量的定義被移動(dòng)到函數(shù)的頂部。因此,使用 var
聲明的變量可以在包含函數(shù)的任何位置使用,而不會(huì)生成錯(cuò)誤。如果尚未執(zhí)行初始化代碼,變量的值可能是 undefined
,但是在初始化之前仍然可以使用變量,而不會(huì)引發(fā)錯(cuò)誤(這可能是錯(cuò)誤的來源之一,也是 let
解決的最重要的錯(cuò)誤傾向特性之一。如果使用 let
聲明變量并嘗試在 let
語句運(yùn)行之前使用它,您將得到錯(cuò)誤而不是 undefined
值)。
使用未聲明的變量。
在嚴(yán)格模式下,嘗試使用未聲明的變量會(huì)在運(yùn)行時(shí)觸發(fā)引用錯(cuò)誤。然而,在嚴(yán)格模式之外,如果將值賦給未經(jīng) let
、const
或 var
聲明的名稱,將創(chuàng)建一個(gè)新的全局變量。此外,不管賦值語句在函數(shù)或代碼塊內(nèi)嵌套多深,它都會(huì)創(chuàng)建一個(gè)全局變量。這種行為當(dāng)然是不可取的,因?yàn)樗赡軐?dǎo)致缺陷,是使用嚴(yán)格模式的一個(gè)強(qiáng)有力的理由。以這種意外方式創(chuàng)建的全局變量的行為類似于使用 var
聲明的全局變量,因?yàn)樗鼈兌x了全局對象的屬性。但是,與通過適當(dāng)?shù)?var
聲明定義的屬性不同,這些屬性可以使用 delete
操作刪除。
(程序員的軟技能:ke.qq.com/course/6034346)
3. 解構(gòu)賦值。
ES6 引入了一種復(fù)合聲明和賦值語法,稱為 "解構(gòu)賦值"。在解構(gòu)賦值中,等號右側(cè)的值是一個(gè)數(shù)組或?qū)ο螅ㄒ粋€(gè) "結(jié)構(gòu)化" 值),而左側(cè)模擬了數(shù)組或?qū)ο笞置嬲Z法以指定一個(gè)或多個(gè)變量。當(dāng)發(fā)生解構(gòu)賦值時(shí),一個(gè)或多個(gè)值從右側(cè)的值中提?。?#34;解構(gòu)")并保存到左側(cè)列出的變量中。解構(gòu)賦值通常用于初始化 const
、let
或 var
聲明中的變量,但也可以用于常規(guī)賦值表達(dá)式(用于將值賦給先前聲明的變量)。此外,在定義函數(shù)參數(shù)時(shí)也可以使用解構(gòu)賦值。
以下是演示數(shù)組值解構(gòu)的代碼片段:
解構(gòu)賦值極大地簡化了返回?cái)?shù)組的函數(shù)的使用:
如前所述,變量和常量可以在各種 JavaScript for
循環(huán)中聲明和使用。在這個(gè)情境下,變量解構(gòu)也同樣適用。下面的代碼遍歷對象的所有屬性名稱/值對,并使用解構(gòu)賦值將一個(gè)兩元素?cái)?shù)組轉(zhuǎn)換為單個(gè)變量:
在解構(gòu)賦值中,左側(cè)的變量數(shù)目不必與右側(cè)數(shù)組的元素?cái)?shù)目完全匹配。左側(cè)多余的變量將被賦值為 undefined
,而右側(cè)多余的值將被忽略。左側(cè)的變量列表可以包含額外的逗號,以跳過右側(cè)的某些值:
使用解構(gòu)賦值時(shí),如果要將所有未使用或剩余的值收集到一個(gè)變量中,可以在左側(cè)的最后一個(gè)變量名前放置三個(gè)點(diǎn)(...
):
解構(gòu)賦值也可以用于嵌套數(shù)組。在這種情況下,賦值的左側(cè)應(yīng)該類似于嵌套的數(shù)組字面值:
數(shù)組解構(gòu)的一個(gè)令人印象深刻的特性是,它不僅限于數(shù)組!實(shí)際上,賦值的右側(cè)可以是任何可迭代對象,包括可以在 for/of
循環(huán)中使用的對象:
解構(gòu)賦值也適用于右側(cè)為對象值的情況。在這種情況下,左側(cè)類似于一個(gè)對象字面值,其中包含用花括號括起的用逗號分隔的變量名列表:
下面的示例演示了將 Math
對象的全局函數(shù)復(fù)制到變量中,以便更輕松地處理復(fù)雜的三角計(jì)算:
請記住,上述代碼中 Math
對象的屬性遠(yuǎn)遠(yuǎn)不止三個(gè)被解構(gòu)的變量。沒有按名稱提及的屬性將被忽略。如果解構(gòu)賦值的左側(cè)包含一個(gè)不是 Math
屬性的變量名,那么該變量將被賦值為 undefined
。
在上述對象解構(gòu)的每個(gè)示例中,我們選擇了與被解構(gòu)對象的屬性匹配的變量名。這保持了簡單和易于理解的語法,但并不是嚴(yán)格必需的。對象解構(gòu)賦值的左側(cè)的每個(gè)標(biāo)識符都可以是一個(gè)由冒號分隔的對,其中第一個(gè)標(biāo)識符是要解構(gòu)的屬性名稱,第二個(gè)標(biāo)識符是要將值賦給的變量:
需要注意的是,變量名與屬性名不同時(shí),對象解構(gòu)的語法變得過于復(fù)雜,從而使其變得不太實(shí)用。在這種情況下,我通常避免使用縮寫形式。如果選擇使用它,請記住屬性名始終必須在冒號的左側(cè),無論是在對象字面值中還是在對象解構(gòu)賦值中。
使用嵌套對象、對象內(nèi)的數(shù)組或?qū)ο髢?nèi)的數(shù)組可以使解構(gòu)賦值變得更加復(fù)雜,但它仍然是有效的:
如果解構(gòu)的不是對象數(shù)組,還可以解構(gòu)對象中的數(shù)組:
類似這樣的復(fù)雜解構(gòu)語法可能難以編寫和理解,通常會(huì)使傳統(tǒng)的代碼(如 let x1 = points.p1[0];
(程序員的軟技能:ke.qq.com/course/6034346)