塊級作用域綁定
var聲明及變量提升機制
在ES6之前,在函數作用域中或者全局作用域中通過var
關鍵字來聲明變量,無論是在代碼的哪個位置,這條聲明語句都會提到最頂部來執行,這就是變量聲明提升。
注意:只是聲明提升,初始化並沒有提升。
function getStudent(name){
if(name){
var age=25;
}else{
console.log("name不存在");
}
console.log(age); //undefined
}
如果按照預想的代碼的執行順序,當name
有值時纔會創建變量age
,可是執行代碼發現,即使不傳入name
,判斷語句外的輸出語句並沒有報錯,而是輸出undefined
。
這就是變量聲明提升。
塊級聲明
ES6前是沒有塊級作用域的,比如{}
外可以訪問內部的變量。
let聲明
- 聲明變量
- 作用域限制在當前代碼塊
- 聲明不會提升
- 禁止重聲明(同一作用域不行,可以覆蓋外部同名變量)
function getStudent(name){
if(name){
let age=25;
console.log(age); //25
}else{
console.log("name不存在");
}
console.log(age); //age is not defined
}
和上文一樣的代碼,只是將age
的命名關鍵字從var
改成了let
,在執行getStudent()
和getStudent("axuebin")
時都會報錯。
原因:
- 在if語句內部執行之後,
age
變量將立即被銷燬 - 如果
name
爲空,則永遠都不會創建age
變量
const聲明
- 聲明常量
- 必須初始化
- 不可更改
- 作用域限制在當前代碼塊
- 聲明不會提升
- 禁止重聲明(同一作用域不行,可以覆蓋外部同名變量)
如果用const
來聲明對象,則對象中的值可以修改。
臨時死區(Temporal Dead Zone)
JavaScript引擎在掃面代碼發現聲明變量時,遇到var
則提升到作用域頂部,遇到let
和const
則放到TDZ中。當執行了變量聲明語句後,TDZ中的變量才能正常訪問。
循環中的塊作用域綁定
我們經常使用for循環:
for(var i=0;i<10;i++){
console.log(i); //0,1,2,3,4,5,6,7,8,9
}
console.log(i) //10
發現了什麼?
在for循環執行後,我們仍然可以訪問到變量i
。
把var
換成let
就解決了
for(let i=0;i<10;i++){
console.log(i); //0,1,2,3,4,5,6,7,8,9
}
console.log(i) //i is not defined
講閉包時setTimeout循環各一秒輸出i的jin例子
for(var i=0;i<10;i++){
setTimeout(function(){
console.log(i); //10,10,10.....
},1000)
}
很顯然,上面的代碼輸出了10次的10,setTimeout
在執行了循環之後才執行,此時i
已經是10了~
之前,我們這樣做 ~
for(var i=0;i<10;i++){
setTimeout((function(i){
console.log(i); //0,1,2,3,4,5,6,7,8,9
})(i),1000)
}
現在,我們這樣做 ~ 來看看把var
改成let
會怎樣~
for(let i=0;i<10;i++){
setTimeout(function(){
console.log(i); //0,1,2,3,4,5,6,7,8,9
},1000)
}
全局塊作用域綁定
- 全局對象是最頂層的對象,在瀏覽器環境指的是window對象,在 Node.js 指的是global對象。 ES5 之中,全局對象的屬性與全局變量是等價的。
window.a = 1;
a // 1
a = 2;
window.a // 2
- 未聲明的全局變量,自動成爲全局對象window的屬性,這被認爲是 JavaScript 語言最大的設計敗筆之一。
- ES6 爲了改變這一點,一方面規定,爲了保持兼容性,var命令和function命令聲明的全局變量,依舊是全局對象的屬性;
- 另一方面規定,let命令、const命令、class命令聲明的全局變量,不屬於全局對象的屬性。也就是說,從 ES6 開始,全局變量將逐步與全局對象的屬性脫鉤。
var a = 1;
// 如果在 Node 的 REPL 環境,可以寫成 global.a
// 或者採用通用方法,寫成 this.a
window.a // 1
let b = 1;
window.b // undefined
- 上面代碼中,全局變量a由var命令聲明,所以它是全局對象的屬性;全局變量b由let命令聲明,所以它不是全局對象的屬性,返回undefined