注意,寫在開頭
function test(x = 1) {var x // 不報錯console.log(x)
}
function test1(x = 1) {let x = 10 // 報錯console.log(x)
}
let的變量名不可以和參數中的名稱相同。而var并不限制,說白了就是希望你規范使用變量名。
形參原則上數組函數內部的臨時變量,但是形參其實在內存中有獨立的空間存儲。
阮一峰ES6 - let
思考,在編寫代碼時,有es5、es6的語法,究竟是否有塊級作用域?
ES6 明確規定,如果區塊中存在let和const命令,這個區塊對這些命令聲明的變量,從一開始就形成了封閉作用域。
1. var關鍵字可以重復聲明
var num1 = 1;
var num1 = 100;
console.log(num1) // 100
2. let塊級作用域
2.1 同一作用域下不能重復聲明(無論是let/var/const聲明)
let num1 = 1;
var num1 = 100; // 報錯 重復定義
var a = 10;
let a = 10; // 報錯
function test(a){let a = 10; // 預編譯時,形參a已定義,重復聲明報錯
}
let a = 1;
function a() { } // 報錯
let a = 1;
{function a() { } // 不報錯
}
// 函數提升是在當前(塊級)作用域下提升
// 轉譯后
"use strict";var a = 1;
{var _a = function _a() {}; // 不報錯}
function test(a) {{let a = 10;}console.log(a) // undefined
}
test()
2.2 let不會提升,會產生一個暫時性死區,在變量聲明前訪問會報錯
var a = a;
console.log(a) // undefined
let x = x; // 報錯
// 在變量x的聲明語句還沒有執行完成前,就去取x的值,導致報錯”x 未定義“。
// 這里不要把賦值語句拆分為聲明和賦值2步理解
- 以下3種情況
var x = 1;
{// 此處=右的x取的是塊級作用域內的// 暫時性死區let x = x;console.log(x) // 報錯// Uncaught ReferenceError: Cannot access 'x' before initialization
}
↓
// 轉譯ES5
"use strict";var x = 1;
{var _x = _x;console.log(_x); // undefined
}
這里注意: 因為es5不存在暫時死區。let x = x 的問題在于,右側x是取值并賦值的操作,而這個時候在es6里,x并沒有完成初始化,所以取值x的時候就會失敗。
而var是不存在這種問題的。因為es6嚴格規定了初始化流程就是變量聲明必須初始化且不允許取值。也就是說聲明語句不可以有對該變量的引用。
轉譯并不能百分百還原。對應let轉var這里就出現了以上的情況。
有些瀏覽器不支持塊級作用域(大括號)。
轉譯結果和babel的版本也有關系,有可能轉譯后去除了大括號或轉成IIFE或其他形式。
var x = 1;
{let x;x = x;console.log(x) // undefined
}
↓
"use strict";var x = 1;
{var _x;_x = _x;console.log(_x); // undefined
}
// 這個本身就是ES5不需要轉
var x = 1;
{x = x;console.log(x) // 1
}
let a ;
a = a
console.log(a) // undefined
if (true) {// TDZ開始tmp = 'abc'; // ReferenceErrorconsole.log(tmp); // ReferenceErrorlet tmp; // TDZ結束console.log(tmp); // undefinedtmp = 123;console.log(tmp); // 123
}
function bar(x = y, y = 2) {return [x, y];
}bar(); // 報錯
// 參數x默認值等于另一個參數y,而此時y還沒有聲明,屬于“死區”。如果y的默認值是x,就不會報錯,因為此時x已經聲明了。
2.3 typeof不再是一個百分之百安全的操作
typeof x; // ReferenceError
let x;
注意
for (var i = 0; i < 10; i++) {arr[i] = function () {console.log(i)}
}
for (var i = 0; i < 10; i++) {arr[i]() // 打印0-9
}
// 第二個for循環里,var i重新賦值了,恰好是i
var arr = [];
for (var i = 0; i < 10; i++) {arr[i] = function () {console.log(i)}
}
// 上個for循環var i是全局的,退出循環后為10
for (var index = 0; index < 10; index++) {arr[index]() // 10個10
}
==================================================
let是在塊中聲明的變量,每當聲明一個function都會傳入當前for的塊中單獨的i進去。也就是說let是塊變量,在自己的塊,或者子塊中使用。而不是只在函數或全局作用域中使用。立即執行函數是為了將每次的i作為函數作用域中的局部變量傳入與外界的i隔離。
// 用let聲明,由于存在父子級作用域,相當于也形成了閉包
var arr = [];
for (let i = 0; i < 10; i++) {arr[i] = function () {console.log(i)}
}
// 即使index用let打印的也是0-9
for (var index = 0; index < 10; index++) {arr[index]() // 0-9
}
// 轉譯之后
"use strict";var arr = [];var _loop = function _loop(i) {arr[i] = function () {console.log(i);};
};for (var i = 0; i < 10; i++) {_loop(i); // 這里立即執行了
}for (var index = 0; index < 10; index++) {arr[index](); // 0-9
}
2.4 for循環作用域
- for循環的特別之處,就是設置循環變量的那部分是一個父作用域,而循環體內部是一個單獨的子作用域
for (var i = 0; i < 10; i++) {let i = 'a' console.log(i) // 10個a // 循環的index i是聲明在全局的
}
console.log(i) // 10
for (let i = 0; i < 10; i++) {// for循環內的塊級作用域// for花括號內的塊級作用域并不相同let i = 'a'console.log(i) // 10個a
}
for (let i = 0; i < 10; i++) {var i = 'a' // 報錯 // 因為for循環內,這里var聲明的i會提升到全局// 而for循環條件又用let聲明一次i,重復聲明了console.log(i)
}
if (1) {let a = 1;console.log(a) // 1{let a = 10;console.log(a) // 10}
}
以前遇到的有IIFE里不用關鍵字聲明的嗎
let a = 1;
(function(){a = 10; console.log(a) // 10
})()
console.log(a) // 10
let a = 1;
(function(){let a = 10;console.log(a) // 10
})()
console.log(a) // 1
var a;
(function () {a = 10;console.log(a) // 10
})();
(function () {a = 100;console.log(a) // 100
})();
console.log(a) // 100
(function () {var a = 10;console.log(a) // 10
})();
(function () {var a = 100;console.log(a) // 100
})();
console.log(a) // 報錯
// Uncaught ReferenceError: a is not defined
思考:在index.html文件的script標簽里編碼,既有es5的語法,又有es6的語法,在瀏覽器中打開時,瀏覽器會將所有代碼轉譯成es6嗎
不會轉譯,瀏覽器不同的版本對es的支持不一樣。(現代瀏覽器基本都支持ES6)
現在普遍兼容es6的語法。但對特殊的語法需要babel轉譯, 包括對象的拓展,類的修飾等等,這些是需要babel轉譯的。
塊級作用域等于匿名函數的立即調用嗎?并不,塊級作用域沒有返回值。二者本質不同。
思考總結,在塊級作用域{}內,用let/const聲明的和父作用域同名的變量
x
,在轉譯ES6的時候,會被編譯成另一變量_x