《關於ES6的學習》
var
JavaScript中,我們通常說的作用域是函數作用域,使用var聲明的變量,無論是在代碼的哪個地方聲明的,都會提升到當前作用域的最頂部,這種行為叫做變量提升(Hoisting)
也就是說,如果在函數內部聲明的變量,都會被提升到該函數開頭,而在全局聲明的變量,就會提升到全局作用域的頂部。
function test() {
console.log(‘1: ‘, a) //undefined
if (false) {
var a = 1
}
console.log(‘3: ‘, a) //undefined
}
test()
實際執行時,上面的代碼中的變量a會提升到函數頂部聲明,即使if語句的條件是false,也一樣不影響a變量提升。
function test() {
var a
//a聲明沒有賦值
console.log(‘1: ‘, a) //undefined
if (false) {
a = 1
}
//a聲明沒有賦值
console.log(‘3: ‘, a) //undefined
}
在函數嵌套函數的場景下,變量只會提升到最近的一個函數頂部,而不會。
//b提升到函數a頂部,但不會提升到函數test。
function test() {
function a() {
if (false) {
var b = 2
}
}
console.log(‘b: ‘, b)
}
test() //b is not defined
如果a沒有聲明,那麽就會報錯,沒有聲明和聲明後沒有賦值是不一樣的,這點一定要區分開,有助於我們找bug。
//a沒有聲明的情況
a is not defined
let
let和const都能夠聲明塊級作用域,用法和var是類似的,let的特點是不會變量提升,而是被鎖在當前塊中。
一個非常簡單的例子:
function test() {
if(true) {
console.log(a)//TDZ,俗稱臨時死區,用來描述變量不提升的現象
let a = 1
}
}
test() // a is not defined
function test() {
if(true) {
let a = 1
}
console.log(a)
}
test() // a is not defined
唯一正確的使用方法:先聲明,再訪問。
function test() {
if(true) {
let a = 1
console.log(a)
}
}
test() // 1
const
聲明常量,一旦聲明,不可更改,而且常量必須初始化賦值。
const type = "ACTION"
我們試試重新聲明type,看看會報什麽錯:
const type = "ACTION"
type = 1
console.log(type) //"type" is read-only
const type = "ACTION"
let type = 1
console.log(type) //Duplicate declaration "type"
const雖然是常量,不允許修改默認賦值,但如果定義的是對象Object,那麽可以修改對象內部的屬性值。
const type = {
a: 1
}
type.a = 2 //沒有直接修改type的值,而是修改type.a的屬性值,這是允許的。
console.log(type) // {a: 2}
const和let的異同點
相同點:const和let都是在當前塊內有效,執行到塊外會被銷毀,也不存在變量提升(TDZ),不能重復聲明。
不同點:const不能再賦值,let聲明的變量可以重復賦值。
臨時死區(TDZ)
上面我們也提到了TDZ的場景,那麽,有什麽用呢?答案就是沒什麽用。
臨時死區的意思是在當前作用域的塊內,在聲明變量前的區域叫做臨時死區。
if (true) {
//這塊區域是TDZ
let a = 1
}
塊級作用域的使用場景
除了上面提到的常用聲明方式,我們還可以在循環中使用,最出名的一道面試題:循環中定時器閉包的考題
在for循環中使用var聲明的循環變量,會跳出循環體汙染當前的函數。
for(var i = 0; i < 5; i++) {
setTimeout(() => {
console.log(i) //5, 5, 5, 5, 5
}, 0)
}
console.log(i) //5 i跳出循環體汙染外部函數
//將var改成let之後
for(let i = 0; i < 5; i++) {
setTimeout(() => {
console.log(i) // 0,1,2,3,4
}, 0)
}
console.log(i)//i is not defined i無法汙染外部函數
在全局作用域聲明
如果在全局作用域使用let或者const聲明,當聲明的變量本身就是全局屬性,比如closed。只會覆蓋該全局變量,而不會替換它。
window.closed = false
let closed = true
closed // true
window.closed // false
最佳實踐
在實際開發中,我們選擇使用var、let還是const,取決於我們的變量是不是需要更新,通常我們希望變量保證不被惡意修改,而使用大量的const,在react中,props傳遞的對象是不可更改的,所以使用const聲明,聲明一個對象的時候,也推薦使用const,當你需要修改聲明的變量值時,使用let,var能用的場景都可以使用let替代。
《關於ES6的學習》