JavaScript:var、let、const之間的差異解析
2.9k 字,約 3 分鐘
作用域(Scope)的差別
作用域(Scope)是指在程式碼中變數和函式的可訪問範圍
var 函數作用域(function scope)
- 變數在函數中的任何地方都是可讀取的。
- 如果在函數之外的地方嘗試訪問變數,將會顯示該變數不存在ReferenceError的錯誤
- 變數作用域說明
- 第一個 var a = 20(作用域:1-3行)
- 第二個 var a = 30(作用域:4-6行)
- 範例如下
function funcA() {
var a = 20
}
function funcB() {
var a = 30
}
console.log(a)
顯示如下
Uncaught ReferenceError: a is not defined
全域變數擁有全域作用域(Global Scope)
- var變數在函式(Function)外宣告時,該變數會自動成為全域物件屬性。
- let宣告的變數不會成為全域物件屬性。其作用域僅限於宣告它的區塊(block scope)或函式內部。
範例如下:
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Test</title>
<script src="https://cdnjs.cloudflare.com/ajax/libs/axios/1.6.2/axios.min.js"></script>
</head>
<body>
<script>
var a = 10
let b = 20
console.log(window)
</script>
</body>
</html>
- 將全域物件window印出,可以看到剛剛設定的a已經變成window的一種屬性
若跳過宣告的流程,宣告的變數也會變成全域物件屬性
範例如下:
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Test</title>
<script src="https://cdnjs.cloudflare.com/ajax/libs/axios/1.6.2/axios.min.js"></script>
</head>
<body>
<script>
function test(){
a = 100
}
test()
console.log(window)
</script>
</body>
</html>
跳過宣告的情形下,變成Window全局屬性
let/const 塊級作用域(block scope)
- 變數在聲明的區塊{}(例如,if 語句、迴圈)內可讀取的
- 如果在區塊之外的地方嘗試訪問變數,將會顯示該變數不存在ReferenceError的錯誤
最簡單的定義方式就是單純{}
範例如下
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Test</title>
<script src="https://cdnjs.cloudflare.com/ajax/libs/axios/1.6.2/axios.min.js"></script>
</head>
<body>
<script>
{
const a = 100
console.log(a)
}
</script>
</body>
</html>
顯示如下
搭配以下範例說明let/const 變數作用域
- const Title = "數字太大了"(作用域:2-5行)
- 範例如下
function funcA(Value) {
if (Value > 100) {
const Title = "數字太大了"
console.log("if區域", Title)
}
console.log("我是funcA的最下面", Title)
}
funcA(120)
顯示如下
if區域 數字太大了
Uncaught ReferenceError: Title is not defined
- Title作用域在if的括號之內(紅色框框),因此 if 外層的console.log("我是funcA的最下面", Title)無法正確讀取到Title變數,出現ReferenceError錯誤
const & let 差異
let與const重新賦值差別
- let可以重新賦值,案例如下
let x = 10; // 宣告一個let變數
x = 20 // 合法
- const不可以重新賦值,案例如下
const x = 10 // 宣告一個const變數
x = 20 // 不合法
顯示如下
TypeError: Assignment to constant variable.
const可針對物件型別內容物修改
- const宣告的變數,無法更改原始型別,但可以修改物件型別的屬性
範例如下(物件型別屬性修改)
const a = {
name: "hello",
};
a.name = "Antonio";
console.log(a);
顯示如下
{
name: "Antonio";
}
const常數變數宣告就必須賦值
範例如下(無賦值發生錯誤)
const a;
console.log(a);
顯示如下
SyntaxError: Missing initializer in const declaration
作用鍊(Scope chain)
當使用變數時,依序從當前作用域開始,一層一層往外尋找是否有符合其作用域的變數。這個搜尋的過程形成了作用鍊
試著將上圖的程式碼改成console.log(a + b + c + d)顯示如下
- 因為已經到最外層還是找不到宣告變數d的地方,因此出現ReferenceError錯誤
Uncaught ReferenceError: d is not defined
for迴圈的使用差異
- 情境:使用setTimeout定時器,模擬異步事件。查看透過var及let變數在迴圈中的變化
var定義的index,會彼此共用影響
- var結束的時候,index = 10(全部的index都會被影響)
for (var index = 0; index < 10; index++) {
setTimeout(()=>{
console.log("var index", index)
}, 0)
}
顯示如下
let是重新宣告出來,彼此互不影響(只在單一區塊作用)
for (let index = 0; index < 10; index++) {
setTimeout(()=>{
console.log("let index", index)
}, 0)
}
顯示如下
提升(Hoisting)的差別
JavaScript編譯階段將變數和函式的宣告存入記憶體的概念,使函式和變量的宣告看起來好像被提升到作用域的頂部(實際程式碼的順序不變),但賦值的動作並沒有提升
var變數宣告時會自動存入記憶體並初始化定義undefined
範例如下:
console.log(x)
var x = 100
顯示如下
undefined
實際上瀏覽器看到的是類似以下的順序(並不會真的改變程式碼順序):
var x
console.log(x)
x = 100
- 執行順序上,var變數會優先提升至作用域的頂部並初始化定義為undefined
const/let變數提升時會陷入暫時性死區(Temporal Dead Zone, TDZ)
範例如下:
console.log(x)
let x = 100
顯示如下
ReferenceError: Cannot access 'x' before initialization
透過不同方式更確定let的區塊性提升
範例如下
var a = 10
function test(){
console.log(a)
let a = 20
}
test()
- 因為let的a變數提升,導致console的結果並未顯示10
顯示如下
ReferenceError: Cannot access 'a' before initialization
重複宣告
var變數可以重複宣告
範例如下
var a = 10;
var a = 20;
console.log(a);
顯示如下
20;
- 同樣變數的宣告會被替代,因此最後印出來的是最後一個20
let變數不可以重複宣告
範例如下
let a = 10;
let a = 20;
console.log(a);
顯示如下
SyntaxError: Identifier 'a' has already been declared
- 同一區塊內不可以宣告同樣的變數
留言
留言使用 GitHub 帳號登入(Giscus),留言會存在 GitHub Discussions。