一.Babel轉換器
Babel 轉碼器,能將ES6代碼轉換成ES5。例如:
// 轉碼前input.map(item => item + 1);
// 轉碼後input.map(function (item) {
return item + 1;});
babel=browser.js可以通過進入browser.js的方式轉換代碼。
二.爲什麼需要塊級作用域?
ES5 只有全局作用域和函數作用域,沒有塊級作用域,這帶來很多不合理的場景。
第一種場景,內層變量可能會覆蓋外層變量。
var tmp = new Date();
function f() {
console.log(tmp);
if (false) {
var tmp = 'hello world';
}}
f(); // undefined
解釋:if代碼塊的外部使用外層的tmp變量,內部使用內層的tmp變量。但是,函數f執行後,輸出結果爲undefined,原因在於變量提升,導致內層的tmp變量覆蓋了外層的tmp變量。
第二種場景,用來計數的循環變量泄露爲全局變量。
var s = 'hello';
for (var i = 0; i < s.length; i++) {
console.log(s[i]);
}
console.log(i); // 5
解釋:變量i只用來控制循環,但是循環結束後,它並沒有消失,泄露成了全局變量。
三.塊級作用域與函數聲明
ES5 規定,函數只能在頂層作用域和函數作用域之中聲明,不能在塊級作用域聲明。ES6可以。
// 情況一if (true) {
function f() {}}
// 情況二try {
function f() {}} catch(e) {
// ...}
根據 ES5 的規定都是非法的。但是,瀏覽器沒有遵守這個規定,爲了兼容以前的舊代碼,還是支持在塊級作用域之中聲明函數,因此上面兩種情況實際都能運行,不會報錯。
function f() { console.log('I am outside!'); }
(function () {
if (false) {
// 重複聲明一次函數f
function f() { console.log('I am inside!'); }
}
f();
}());
上面代碼在 ES5 中運行,會得到“I am inside!”,因爲在if
內聲明的函數f
會被提升到函數頭部,實際運行的代碼如下:
// ES5 環境
function f() { console.log('I am outside!'); }
(function () {
function f() { console.log('I am inside!'); }
if (false) {
}
f();
}());
ES6 就完全不一樣了,理論上會得到“I am outside!”。因爲塊級作用域內聲明的函數類似於let
,對作用域之外沒有影響。但是,如果你真的在 ES6 瀏覽器中運行一下上面的代碼,是會報錯的,這是爲什麼呢?ES6:
- 允許在塊級作用域內聲明函數。
- 函數聲明類似於
var
,即會提升到全局作用域或函數作用域的頭部。 - 同時,函數聲明還會提升到所在的塊級作用域的頭部。
根據這三條規則,在瀏覽器的 ES6 環境中,塊級作用域內聲明的函數,行爲類似於var
聲明的變量。
// 瀏覽器的 ES6 環境
function f() { console.log('I am outside!'); }
(function () {
if (false) {
// 重複聲明一次函數f
function f() { console.log('I am inside!'); }
}
f();
}());
// Uncaught TypeError: f is not a function
考慮到環境導致的行爲差異太大,應該避免在塊級作用域內聲明函數。如果確實需要,也應該寫成函數表達式,而不是函數聲明語句。
// 函數聲明語句
{
let a = 'secret';
function f() {
return a;
}
}
// 函數表達式
{
let a = 'secret';
let f = function () {
return a;
};
}
另外,還有一個需要注意的地方。ES6 的塊級作用域允許聲明函數的規則,只在使用大括號的情況下成立,如果沒有使用大括號,就會報錯。
// 不報錯
'use strict';
if (true) {
function f() {}
}
// 報錯
'use strict';
if (true)
function f() {}
四.ES6聲明變量的6種方法
var聲明的侷限性:
(1)可以重複聲明
(2)無法限制修改
(3)沒有塊級作用域
ES5 只有兩種聲明變量的方法:var命令和function命令。ES6 除了添加let和const命令,另外兩種聲明變量的方法:import命令和class命令。所以,ES6 一共有 6 種聲明變量的方法。
五.let命令
報錯:undefined定義變量但沒賦值 referenceError變量沒定義
ES5沒有塊級作用域 let實際上爲 JavaScript 新增了塊級作用域。
1.let聲明的變量只在它所在的代碼塊內有效
{
let a = 10;
var b = 1;
}
a // ReferenceError: a is not defined.
b // 1
for循環的計數器,就很合適使用let命令。
for (let i = 0; i < 10; i++) {
// ...}
console.log(i);
// ReferenceError: i is not defined
例:
var a = [];for (var i = 0; i < 10; i++) {
a[i] = function () {
console.log(i);
};}
a[6](); // 10
var a = [];for (let i = 0; i < 10; i++) {
a[i] = function () {
console.log(i);
};}
a[6](); // 6
解釋:變量i是var命令聲明的,在全局範圍內都有效,所以全局只有一個變量i。每一次循環,變量i的值都會發生改變,而循環內被賦給數組a的函數內部的console.log(i),裏面的i指向的就是全局的i。也就是說,所有數組a的成員裏面的i,指向的都是同一個i,導致運行時輸出的是最後一輪的i的值,也就是 10。如果使用let,聲明的變量僅在塊級作用域內有效,最後輸出的是 6。
2.不存在變量提升(let、const語句不存在變量提升,主要是爲了減少運行時錯誤)
// var 的情況console.log(foo); // 輸出undefinedvar foo = 2;
// let 的情況console.log(bar); // 報錯ReferenceErrorlet bar = 2;
3.暫時性死區(在代碼塊內,使用let命令聲明變量之前,該變量都是不可用的。這在語法上,稱爲“暫時性死區”)
var tmp = 123;
if (true) {
tmp = 'abc'; // ReferenceError
let tmp;
}
4.不允許重複聲明(let不允許在相同作用域內,重複聲明同一個變量。)
// 報錯function func() {
let a = 10;
var a = 1;
}
// 報錯function func() {
let a = 10;
let a = 1;
}
function func(arg) {
let arg; // 報錯
}
function func(arg) {
{
let arg; // 不報錯
}
六.const命令
1.const聲明一個只讀的常量。一旦聲明,常量的值就不能改變。
2.const一旦聲明變量,就必須立即初始化,不能留到以後賦值。
3.只在聲明所在的塊級作用域內有效。const命令聲明的常量也是不提升,同樣存在暫時性死區,只能在聲明的位置後面使用。const聲明的常量,也與let一樣不可重複聲明。
七.頂層對象屬性
頂層對象,在瀏覽器環境指的是window對象。頂層對象的屬性與全局變量是等價的。
window.a = 1;
a // 1
a = 2;
window.a // 2
ES6 爲了改變這一點,一方面規定,爲了保持兼容性,var命令和function命令聲明的全局變量,依舊是頂層對象的屬性;另一方面規定,let命令、const命令、class命令聲明的全局變量,不屬於頂層對象的屬性。也就是說,從 ES6 開始,全局變量將逐步與頂層對象的屬性脫鉤。
var tmp =new Date();