iT邦幫忙

2021 iThome 鐵人賽

DAY 6
0
Modern Web

我的JavaScript日常系列 第 6

JavaScript Day 6. let or const or var

let 與 var

  • var - 函式作用域
  • letconst- 區塊作用域

JavaScript 宣告變數的方式有三種:var、let、const。let 是 ES6 出現後,被用來改善現有語法的宣告方式,在 ES6 未出現之前,網頁不存在「區塊域」的概念,因此大多數都使用 var 的方式宣告變數,不過這樣有個問題,就是用 var 宣告變數會污染到全域變數,使用 let 可以只在部分區域運作。

var 與 let 宣告變數範例:

var a = '無敵哆啦A夢'; // 這裡的 a 為全域變數
function doraemon () {
	let a = '無敵大雄'; // 這裡的 a 為區域變數
	a = '變身哆啦美';
	console.log(a); // 變身哆啦美
}

doraemon(); 
console.log(a) // 無敵哆啦A夢

從範例來看,結果是「變身哆啦美」與「無敵哆啦A夢」,這裡的觀念在於只存在大括號裡這件事。

第一個 console.log(a) 是「變身哆啦美」,原因是因為在裡面我們重新給 a 賦予了一個「變身哆啦美」的值,而到了第二個 console.log(a) 的時候,答案卻變成「無敵哆啦A夢」,這是因為function doraemon 裡面的 a 只會存活在 {} 這個大括號裡,因此裡面我們替 a 賦予的值,沒有更改到外面。

接著實際操作 letvar ,看看區塊作用域與函式作用域的差別:

// var 宣告
// 宣告變數 a
var a = 10;
if(true){
  var b = 20;
}
 
// 輸出: a = 10
console.log(a)
// 輸出: b = 20
console.log(b)

// let 宣告
// 宣告變數 a
var a = 10;
 
if(true){
  let b = 20;
}
 
// 輸出: a = 10
console.log(a)
// 因為變數 b 使用 let 宣告,離開 if 區域便無法被存取
console.log(b) //會顯示沒有定義

上面的例子可以看到 varlet 的差異在於區塊的概念,letif 區域裡面宣告的變數只留在 if 括號裡,並且在同一個區塊內,letconst 不能重複宣告變數,var 則可以重複宣告。

var 區域污染的現象在迴圈尤其明顯,這是一個 varfor 的例子:

<ul class="list">
	<li>1</li>
	<li>2</li>
	<li>3</li>
</ul> 
const list = document.querySelectorAll('.list li').length;
for(var i = 0; i < list; i++){
	document.querySelectorAll('.list li')[i].addEventListener('click', function(){
		alert(i+1);
	})
}

以上範例程式碼運作方式為,透過點擊了解我們點擊到的是哪一個按鈕,也就是當我們點擊 <li>1</li> 的時候,我們預期他會顯示 1,點擊 <li>2</li> 的時候預期會顯示 2 。

但在這個範例中會發現,不管我們點擊哪一個,alert 都會顯示 4,這是因為值被「全域變數」影響。for 迴圈跑完以後,會直接顯示全部跑完的結果,而不是個別處理。在這個過程中,var 已經直接將 i 宣告為「全域變數」,並不斷透過 for 進行累加,因此全部跑完的結果是 4,但這不是我們預期的結果。

那要如何讓以上程式範例碼成為我們預期的呢?這時候使用 let 便可以達到個別處理的結果。

const list = document.querySelectorAll('.list li').length;
for(let i = 0; i < list; i++){
	document.querySelectorAll('.list li')[i].addEventListener('click', function(){
		alert(i+1);
	})
}

在這邊我們把 var 改成 let,上面說到 let 有區域特性,它只會在大括號裡運作,因此每一次的執行結果都會顯示一次。

當我們點擊 <li>1</li> 的時候,就會執行一次括號內的程式碼,在 let i = 0 的作用域裡面(括號內)執行 i++ ,因此 alert 此時會跳出 1,當我們點擊 <li>2</li> 的時候,就會再執行一次括號內的程式碼,在 let i = 1 的作用域裡面執行 i++alert 於是跳出 2,然後結束。依此類推,最後即是我們所預期的呈現。

const

const 是宣告常數,常用在一些不能被變更的變數,譬如:url 網址。確定不再做更動的時候,可以使用 const 方法。

嘗試執行以下程式碼呈現的結果:

const me = '齊天大聖孫悟空';
let me = '豬八戒';
// Uncaught SyntaxError: Identifier 'me' has already been declared

在這邊會顯示上面這個 me,已經有被宣告變數,由上面的範例碼可以知道,const 不能再被重新宣告變數覆蓋。

另外,let 可以直接宣告變數,const 則是一定要有值。

let a;
// undefined

const b;
// Uncaught SyntaxError: Missing initializer in const declaration

let 直接宣告變數,只是顯示沒有東西,而直接宣告變數的 const,會顯示缺少值。

雖然使用 const 宣告變數就無法更動,但如果使用 const 宣告物件,裡面的屬性仍然是可以更動的,因為物件有傳參考的特性,因此依然可以修改。

const color = {
	light: 'white',
	dark: 'black'
};
color.midtones = 'grey';
// {light: "white", dark: "black", midtones: "grey"}

最後的結果,midtones: "grey" 是能夠被加上去的。

有一點要注意,已經宣告的物件則無法再被重新宣告。

let newColor = {
	light: 'yellow',
	dark: 'crimson'
};
color = newColor // 錯誤

如果不希望 const 宣告的物件被修改,還有另一項法寶 freeze()freeze() 的功能是防止新增屬性或是屬性遭到修改,就像它英文的意思一樣,「凍結」屬性。

const color = {
	light: 'white',
	dark: 'black'
};
Object.freeze(color);
color.midtones = 'grey';
// {light: "white", dark: "black"}

同樣的範例,可以看到與上面的結果不同,在物件下方添加 freeze(),屬性 midtones 則無法被加入,於是可以防止物件被添加或修改。

參考資料:
ES6 開始的新生活 let, const


上一篇
JavaScript Day 5. 型別辨識 typeof
下一篇
JavaScript Day 7. 淺談 Function
系列文
我的JavaScript日常31

尚未有邦友留言

立即登入留言