理解 JavaScript 中的變量、作用域和提升
這篇文章最初是為 DigitalOcean 寫的。
簡介
變量 是一個基本的編程概念,也是首先要學習的東西之一。在 JavaScript 中,有三種方式來聲明變量 - 使用關鍵字 var
, let
, 和 const
.
在這篇文章中,我們將學習什麼是變量,如何聲明和命名它們,var
的區別 , let
, 和 const
,以及全局和局部範圍的意義。
理解變量
變量是用於存儲值的命名容器。我們可能會多次引用的一條信息可以存儲在一個變量中以供以後使用或修改。
代數中的變量,經常用 x
表示 , 用於保存未知數的值。在 JavaScript 中,變量中包含的值可以不僅僅是一個數字;它可以是任何 JavaScript 數據類型,例如字符串或對象。
在 JavaScript 所基於的 ECMAScript 2015 (ES6) 語言規範之前,只有一種聲明變量的方法 - 使用 var
關鍵詞。因此,大多數較舊的代碼和學習資源將僅使用 var
對於變量,使其成為學習的重要關鍵字,即使是新關鍵字 let
和 const
被引入語言。
我們可以使用 var
來演示變量本身的概念。在下面的例子中,我們將聲明 一個變量,然後賦值 給它一個價值。
// Assign the string value Sammy to the username identifier
var username = 'sammy_shark'
該語句由幾個部分組成:
- 使用
var
聲明變量 關鍵詞 - 變量名(或標識符),
username
- 賦值操作,由
=
表示 語法 - 被賦值的值,
"sammy_shark"
現在我們可以使用 username
在代碼中,JavaScript 會記住 username
表示字符串值sammy_shark
.
// Check if variable is equal to value
if (username === 'sammy_shark') {
console.log(true)
}
true
變量可用於表示 JavaScript 中的所有數據類型。在本例中,我們將字符串、數字、對象、布爾值和空值傳遞給變量。
// Assignment of various variables
var name = 'Sammy'
var spartans = 300
var kingdoms = ['mammals', 'birds', 'fish']
var poem = { roses: 'red', violets: 'blue' }
var success = true
var nothing = null
使用 console.log
,我們可以看到特定變量中包含的值。
// Send spartans variable to the console
console.log(spartans)
300
變量將數據存儲在內存中,以後可以訪問和修改。也可以重新分配變量並賦予新值。在下面的簡化示例中,我們可以演示如何將密碼存儲到變量中並進行更新。
// Assign value to password variable
var password = 'hunter2'
// Reassign variable value with a new value
password = 'hunter3'
console.log(password)
'hunter3'
在實際程序中,密碼很可能安全地存儲在數據庫中,但這個示例可以演示我們可能需要更新變量值的情況。 password
的值 是 hunter2
,但我們將其重新分配給 hunter3
從那時起,這就是 JavaScript 所認可的價值。
命名變量
變量名被稱為標識符 在 JavaScript 中。我們在Understanding Syntax and Code Structure in JavaScript中討論了一些命名標識符的規則。以下是一些必須遵守的規則。
- 變量名可以由字母組成(
a-z
), 數字 (0-9
), 美元符號 ($
) 和下劃線 (_
) - 變量名稱不得包含空格(製表符或空格)
- 變量名不能以數字開頭
- 命名的變量不能包含任何保留關鍵字
- 變量名區分大小寫
JavaScript 也有使用駝峰式(有時稱為駝峰式)的慣例,即第一個單詞小寫,後面的所有單詞大寫。大多數標識符都會遵循這個約定,但也有一些例外。
這似乎需要學習很多規則,但編寫有效和常規的變量名很快就會成為第二天性。
範圍
範圍 在 JavaScript 中是指代碼的當前上下文,它決定了變量對 JavaScript 的可訪問性。這兩種範圍是本地 和全局 .
全局變量是在塊之外聲明的變量。局部變量是在塊內聲明的變量。在下面的示例中,我們將創建一個全局變量。
// Initialize a global variable
var creature = 'wolf'
我們了解到可以重新分配變量。使用局部作用域,我們實際上可以創建與外部作用域中的變量同名的新變量,而無需更改或重新分配原始值。
在下面的示例中,我們將創建一個全局 species
多變的。函數內部是一個同名的局部變量。通過將它們發送到控制台,我們可以看到變量的值如何根據作用域不同而不同,並且原始值沒有改變。
// Initialize a global variable
var species = 'human'
function transform() {
// Initialize a local, function-scoped variable
var species = 'werewolf'
console.log(species)
}
// Log the global and local variable
console.log(species)
transform()
console.log(species)
human
werewolf
human
在這個例子中,局部變量是 function-scoped .用 var
聲明的變量 關鍵字是函數範圍的,這意味著它們僅將函數識別為單獨的範圍。本地範圍的變量將無法從全局範圍訪問。
新關鍵字let
和 const
是塊作用域 ,這意味著一個新的本地範圍不僅是從功能塊創建的,而且是從任何其他塊創建的。 JavaScript 中的其他類型的塊由關鍵字組成,例如 if
, for
, 和 while
.
為了演示函數和塊範圍變量之間的區別,我們將在 if
中分配一個新變量 使用 let
阻止 .
var fullMoon = true
// Initialize a global variable
let species = 'human'
if (fullMoon) {
// Initialize a block scoped variable
let species = 'werewolf'
console.log(`It is a full moon. Lupin is currently a ${species}.`)
}
console.log(`It is not a full moon. Lupin is currently a ${species}.`)
It is a full moon. Lupin is currently a werewolf.
It is not a full moon. Lupin is currently a human.
在此示例中,species
變量全局只有一個值(human
) 和本地的另一個值 (werewolf
)。如果我們使用 var
,但是,會有不同的結果。
// Use var to initialize a variable
var species = 'human'
if (fullMoon) {
// Attempt to create a new variable in a block
var species = 'werewolf'
console.log(`It is a full moon. Lupin is currently a ${species}.`)
}
console.log(`It is not a full moon. Lupin is currently a ${species}.`)
It is a full moon. Lupin is currently a werewolf.
It is not a full moon. Lupin is currently a werewolf.
在此示例的結果中,全局變量和塊作用域變量都以相同的值結束,werewolf
.這是因為沒有使用 var
創建新的局部變量 ,您在同一範圍內重新分配相同的變量。 var
看不懂if
成為不同的新範圍的一部分。
總而言之,作用域是變量對 JavaScript 的可見性。全局作用域是作用域的最外層上下文,局部作用域是更具體的作用域。有兩種類型的局部作用域——函數作用域和塊作用域。 var
僅限於函數作用域,這意味著只能在函數內部創建新作用域。 let
和 const
具有塊作用域,這意味著任何塊都會創建一個新的本地作用域,例如 if
, for
, 和 while
.塊作用域更安全,因為它生成的代碼不太可能無意中覆蓋變量值。
吊裝
到目前為止,在大多數示例中,我們都使用了 var
聲明 一個變量,我們已經初始化了 它具有價值。聲明和初始化後,我們可以訪問或重新分配變量。
如果我們在聲明和初始化之前嘗試使用變量,將返回 undefined
.
// Attempt to use a variable before declaring it
console.log(x)
// Variable assignment
var x = 100
undefined
但是,如果我們省略 var
關鍵字,我們不再聲明變量,只是初始化它。它將返回一個 ReferenceError
並停止腳本的執行。
// Attempt to use a variable before declaring it
console.log(x)
// Variable assignment without var
x = 100
ReferenceError: x is not defined
其原因是由於吊裝 ,一個 JavaScript 動作,其中變量和函數聲明被移動到其作用域的頂部。由於只提升了實際聲明,而不是初始化,因此第一個示例中的值返回 undefined
.
為了更清楚地演示,下面是我們編寫的代碼,以及 JavaScript 是如何實際解釋它的。
// The code we wrote
console.log(x)
var x = 100
// How JavaScript interpreted it
var x
console.log(x)
x = 100
JavaScript 保存 x
在執行腳本之前作為變量存儲到內存中。由於在定義之前還是調用了,所以結果是undefined
而不是 100
,但不會導致 ReferenceError
並停止腳本。雖然 var
關鍵字實際上並沒有改變 var
的位置 ,這演示了吊裝的工作原理。
這是一個問題,因為程序員很可能期望 x
的輸出 為 true
, 但它是 undefined
.我們還可以在下面的示例中看到提升如何給我們帶來不可預測的結果。
// Initialize x in the global scope
var x = 100
function hoist() {
// A condition that should not affect the outcome of the code
if (false) {
var x = 200
}
console.log(x)
}
hoist()
undefined
在這個例子中,我們聲明了 x
為 100
全球範圍內。取決於 if
語句,x
可以更改為 200
,但由於條件是 false
,它應該不會影響 x
的值 .相反,x
被吊到hoist()
的頂部 函數,值變成undefined
.
這種不可預測的行為可能會導致程序中的錯誤。自 let
和 const
是塊作用域的,它們不會以這種方式提升,如下所示。
// Initialize x in the global scope
let x = true
function hoist() {
// Initialize x in the function scope
if (3 === 4) {
let x = false
}
console.log(x)
}
hoist()
true
var
可以重複聲明變量 , 將拋出 let
錯誤 和 const
.
// Attempt to overwrite a variable declared with var
var x = 1
var x = 2
console.log(x)
2
// Attempt to overwrite a variable declared with let
let y = 1
let y = 2
console.log(y)
Uncaught SyntaxError: Identifier 'y' has already been declared
總而言之,var
允許提升的可能性,即將變量聲明保存到內存中。這允許代碼中未定義變量的意外後果。 let
的介紹 和 const
通過在聲明變量之前嘗試使用變量或嘗試多次聲明變量時拋出錯誤來解決此問題。
常數
我們已經了解瞭如何使用 var
創建變量 ,我們學習了 let
和 const
解決與範圍和吊裝相關的潛在問題。因此,建議停止使用 var
支持較新的 let
和 const
.而 let
無所不能var
可以,const
還有一些額外的規則要遵循。
許多編程語言都有常量 ,這是不能修改或更改的值。 const
以常量建模,並將值分配給 const
無法重新分配。
// Assign value to const
const SPECIES = 'human'
// Attempt to reassign value
SPECIES = 'werewolf'
console.log(SPECIES)
Uncaught TypeError: Assignment to constant variable.
正在嘗試重新分配 SPECIES
會報錯。
自 const
值不能重新賦值,需要同時聲明和初始化,否則也會拋出錯誤。
// Declare but do not intialize a const
const TODO;
console.log(TODO);
Uncaught SyntaxError: Missing initializer in const declaration
寫所有 const
是常見的約定 標識符全部大寫。這標誌著它們很容易與其他變量值區分開來。
在編程中不能改變的值被稱為不可變 , 而相反的值是可變的 .而 const
不能重新分配,它們不是不可變的,因為可以修改對象屬性。
// Create a CAR object with two properties
const CAR = {
color: 'blue',
price: 15000,
}
// Modify a property of CAR
CAR.price = 20000
console.log(CAR)
{ color: 'blue', price: 20000 }
總而言之,const
值不能重新分配,必須與它們的聲明一起初始化。
Var、Let 和 Const 之間的區別
JavaScript 有三個不同的關鍵字來聲明一個變量,這給語言增加了一層額外的複雜性。三者之間的區別在於範圍、提升和重新分配。
關鍵字 | 範圍 | 吊裝 | 可以重新分配 | 可以重新聲明 |
---|---|---|---|---|
var | 功能範圍 | 是的 | 是的 | 是的 |
let | 塊範圍 | 沒有 | 是的 | 沒有 |
const | 塊範圍 | 沒有 | 沒有 | 沒有 |
您可能想知道應該在自己的程序中使用這三個中的哪一個。一個普遍接受的做法是使用 const
盡可能多的和 let
在循環和重新分配的情況下。一般情況下,var
在處理遺留代碼之外可以避免。
結論
在本文中,我們了解了什麼是變量、變量命名規則以及如何重新分配變量值。我們還了解了範圍和提升,以及原始 var
的一些限制 關鍵字,以及如何let
和 const
糾正這些問題。