使用
在 es6 出現(xiàn)之前月杉, 我們通常使用 var 來申明一個(gè)變量:
var x = 'test'
es6 中新增加了 let 和 const , 他們的作用和 var 相同苛萎,用于申明一個(gè)變量:
let x = 'test'
const Pi = 3.1415926
對(duì)比
約束: const 检号、let 蛙酪、var 約束依次變得松散:
var 對(duì)比 let 和 const :
//正常運(yùn)行
var name = 'js'
var name = 'py'
name // py
//錯(cuò)誤
let name = 'js'
let name = 'py' //Uncaught SyntaxError: Identifier 'name' has already been declared
const name = 'js'
const name = 'py' //Uncaught SyntaxError: Identifier 'name' has already been declared
結(jié)論1: var 申明的變量可以用 var 進(jìn)行重新申明, 而 let 和 const 則不行
//可以對(duì)變量重新賦值
let name = 'js'
name = 'py'
name // py
//無法對(duì)變量重新賦值
const name = 'js'
name = 'py' //Uncaught TypeError: Assignment to constant variable
// 必須在申明變量的時(shí)候就給定一個(gè)值
const a //Missing initializer in const declaration
結(jié)論2: const 必須在申明的時(shí)候就給變量初始化一個(gè)值桂塞,且該變量的值不能改動(dòng)
原因:const 實(shí)際上保證的,并不是變量的值不得改動(dòng)玛痊,而是變量指向的那個(gè)內(nèi)存地址不得改動(dòng)。對(duì)于簡單類型的數(shù)據(jù)(數(shù)值擂煞、字符串趴乡、布爾值)对省,值就保存在變量指向的那個(gè)內(nèi)存地址晾捏,因此等同于常量。但對(duì)于復(fù)合類型的數(shù)據(jù)(主要是對(duì)象和數(shù)組)粟瞬,變量指向的內(nèi)存地址萤捆,保存的只是一個(gè)指針,const只能保證這個(gè)指針是固定的俗或,至于它指向的數(shù)據(jù)結(jié)構(gòu)是不是可變的,就完全不能控制了
變量提升:
var 命令會(huì)發(fā)生”變量提升“現(xiàn)象区匠,var 申明變量會(huì) 其申明語句會(huì)自動(dòng)提升到 當(dāng)前執(zhí)行環(huán)境最前面:
console.log(x) // undefined
var x = 0
console.log(x) // 0
//上面的的語句相當(dāng)于下面語句
var x
console.log(x) // undefined
x = 0
console.log(x) // 0
//let const 不會(huì)出現(xiàn)這種情況
console.log(y) // Uncaught ReferenceError: y is not defined
let y = 0
console.log(z) // Uncaught ReferenceError: z is not defined
const z = 0
結(jié)論3:var 存在變量提升現(xiàn)象帅腌, 而 const let 不存在
塊級(jí)作用域:
在 es6之前,ES5 只有全局作用域和函數(shù)作用域速客,沒有塊級(jí)作用域, 而let提供了塊級(jí)作用域:
var test1 = function() {
var x = 2
if(1){
var x = 3
}
console.log(x)
}
test1() //3
let test2 = function() {
let x = 2
if(1){
let x = 3
}
console.log(x) // 2
}
test2() //2
結(jié)論4:let 會(huì)使得代碼塊擁有自己的作用域溺职, 其內(nèi)部的變量申明不會(huì)干擾外部
關(guān)于塊級(jí)作用域位喂,有一個(gè)常見的面試題:
//(1)再循環(huán)中 延時(shí)輸出數(shù)字
for (var i = 0; i < 5; i++) {
setTimeout(function() {
console.log(i) //會(huì)輸出5個(gè) 5
}, 1000)
}
// (2)如果想要輸出0, 1, 2乱灵,3, 4 可以這樣寫:
for (var i = 0; i < 5; i++) {
(function f1(k) {
setTimeout(function(k) {
console.log( k)
}, 1000)
})(i)
}
//(3)上面的方法是借用閉包形成一個(gè)新的作用域,我們已經(jīng)知道 _let_ 可以形成塊級(jí)作用域痛倚,所以我們可以使用_let_達(dá)到同樣的目的:
for (let i = 0; i < 5; i++) {
setTimeout(function() {
console.log(i)
}, 1000)
}
理解:
- 變量 i 由 var申明, 則循環(huán)每一次都是引用的同一個(gè)i蝉稳, 所以 i 會(huì)變成 5 輸出 5個(gè) 5
- 這里每一次 的 i 被我們傳進(jìn) 一個(gè)立即執(zhí)行函數(shù), 立即執(zhí)行函數(shù)內(nèi)部有新的作用域削锰, 每一次的k都是新的值毕莱, 而每一次的k 存儲(chǔ)了 每一次的 i, 所以輸出 0,1,2,3,4
- 變量i是let聲明的器贩,當(dāng)前的i只在本輪循環(huán)有效朋截,所以每一次循環(huán)的i其實(shí)都是一個(gè)新的變量,其中有一點(diǎn)是部服, javaScript 引擎內(nèi)部會(huì)記住上一輪循環(huán)的值,初始化本輪的變量i時(shí)奉芦,就在上一輪循環(huán)的基礎(chǔ)上進(jìn)行計(jì)算。
對(duì)比總結(jié)
- var 申明的變量可以用 var 進(jìn)行重新申明声功, 而 let 和 const 則不行
- 結(jié)論2: const 必須在申明的時(shí)候就給變量初始化一個(gè)值宠叼,且該變量的值不能改動(dòng)
- let 存在變量提升現(xiàn)象先巴, 而 const let 不存在
- let 會(huì)使得代碼塊擁有自己的作用域冒冬, 其內(nèi)部的變量申明不會(huì)干擾外部
應(yīng)用場景:
- let 可以代替 var 的使用, 可以避免變量的意外重命名沖突
- const 適用于你一次定義简烤,不希望別人再進(jìn)行改動(dòng)的場景,比如config里面的 常值變量抗斤、工具函數(shù) 等等