在ES6之前,JavaScript中,我們通常說的作用域是函數作用域,使用var聲明的變量,無論是在代碼的哪個地方聲明的,都會提升到當前作用域的最頂部,這種行爲叫做變量提升,也就是說,如果在函數內部聲明的變量,都會被提升到該函數開頭,而在全局聲明的變量,就會提升到全局作用域的頂部。它有利有弊,利於方便自由,弊於作用域內的變量容易被共享。
function test() {
console.log('t: ', a) //undefined
if (false) {
var a = 2
}
console.log('s: ', a) //undefined
}
test()
注意這段代碼是寫在<script>標籤裏的;
實際執行時,上面的代碼中的變量a會提升到函數頂部聲明,即使if語句的條件是false,也一樣不影響a變量提升。
function test() {
var a
//a聲明沒有賦值
console.log('t: ', a) //undefined
if (false) {
a = 2
}
//a聲明沒有賦值
console.log('s: ', a) //undefined
}
在函數嵌套函數的場景下,變量只會提升到最近的一個函數頂部,而不會提升到外部函數。
//b提升到函數a頂部,但不會提升到函數test。
function test() {
function a() {
if (false) {
var b = 2
}
}
console.log('b: ', b)
}
test() //b is not defined
如果a沒有聲明,那麼就會報錯,注意:沒有聲明和聲明後沒有賦值是不一樣的,這需要區分開。
//a沒有聲明的情況
a is not defined
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}
type.b = 3 //拓展Object也是沒有問題的
console.log(type) // {a: 2 , b: 3}
delete type.b=3 //刪除整個鍵值對也OK的
console.log(type) // {a: 2}
//如果重新定義數據結構~常量的內存地址值發生改變,這個是不可行的。
type={}; //Assignment to constant variable.
type=[]; //Assignment to constant variable.
const和let的異同點
const和let都是在當前塊內有效,執行到塊外會被銷燬,也不存在變量提升(TDZ),不能重複聲明。
不同點:const不能再賦值,let聲明的變量可以重複賦值。