變數的作用域 Scope 與 var, let, const 差異
- 作用域:變數的生存範圍,==範圍以外就存取不到該變數==
### ES6 以前
- 宣告變數用 var,==其變數生存範圍以 function 為單位==,在 function 以外就存取不到該變數。
var a = 'global'
function test() {
var a = 1
console.log(a)
}
test() // 1
console.log(a) // global
// 這邊存取不到 test() 內的 a = 1
---
var a = 10
function test() {
a = 20
console.log(a) // 20,
// 作用域內找不到 a,所以找到 global 並改成 20
}
test()
console.log(a) // 20
---
// 等於建立 var a
function test() {
a = 20 // 若沒有宣告變數,等於宣告一個 global
console.log(a) // 20
}
test()
console.log(a) // 20
- 在 function 中找不到要存取的變數時,會往更上層找要存取的變數,這個過程就會形成 ==scope chain==。
var a = 'global' // global scope
function test() { // test scope
var b = 'test b'
console.log(a, b)
// 輸出 global, test b,因為在 test 找不到 a,
// 所以往上層找最後找到 global
function inner() { // inner scope
var b = 'inner b'
console.log(a, b)
// 輸出 global, inner b,因為在 inner, test 都找不到 a,
// 所以往上層找最後到 global
}
inner()
}
test()
console.log(a) // global
- 作用域陷阱:==scpoe chain 的形成與 function 本身建立的位置有關,與在哪裡呼叫無關==
var a = "global"
function test() {
var a = "test"
other()
// 雖然在這邊呼叫 other(),
// 但 scope chain 是以 other() 建立時的位置形成
// other scope -> global scope
// 所以輸出 global
// 而不是 other scope -> test scope -> global scope
}
function other() {
console.log(a)
// other() 在這邊建立
// 所以 scope chain 往上層找的 a 就直接找到 global
}
test() // 輸出 global
---
var a = "global"
function test() {
var a = "test"
other()
function other() {
console.log(a)
}
// 因為在這邊建立 other(),所以 scope chain 是
// other scope -> test scope -> global scope
}
test() // 輸出 test
ES6
- 宣告變數用 let, const,==其變數生存範圍以 block {} 為單位==。
function test() {
let a = 5
if (a === 5) {
let b = 10
console.log(b) // 10
}
console.log(b) // error, b is not defined
// 因為超出 {} 範圍,存取不到 b = 10
}
test()
---
function test() {
for(var i = 0; i < 10; i++) {
console.log("loop", i)
}
console.log("final", i)
}
test()
// loop 1, ..., 9
// final 10
function test() {
for(let i = 0; i < 10; i++) {
console.log("loop", i)
}
console.log("final", i)
}
test()
// loop 1, ..., 9
// i is not defined
// 因為 final i 已經超出 block 而存取不到迴圈的 i
var, let, const
- 除了這三個以外,還有一種方式是都不加關鍵字的,這種的會把變數變成全域變數,例如說 a = 3
- var 的 scope 是 function,可以重新宣告與賦值,需注意 hoisting
- let 的 scope 是 block,不能重新宣告,可以重新賦值,有 hoisting + TDZ
- const 的 scope 是 block,宣告時就要給初始值,不能重新賦值但可以在 object 更改內容,有 hoisting + TDZ
const 何時會出錯
- primitive type:判斷數值有沒有改變,有改變就出錯
const a = 10
a = 20
// 錯誤 Assignment to constant variable,不能重新賦值
---
const a
a = 20 // 錯誤,在宣告變數時就要給初始值
- object:判斷記憶體位置有沒有改變,位置改變就出錯
const obj = {number: 1}
obj = {number: 2}
// Assignment to constant variable
// 錯誤,因為建立了新的物件,記憶體位置被改變
const arr = [3]
arr = [4] // 錯誤,建立新物件,記憶體位置被改變
---
const obj = {number: 1}
obj.number = 2
// 可以,因為沒有改變記憶體位置,只是改內部的值
const arr = [3, 4]
arr.push(4) // 可以,沒有改變記憶體位置
arr[0] = 4 // 可以,沒有改變記憶體位置,只是改第 0 個的值
---
tags: Week16