ES6學習筆記(阮一峯教程)——let、const命令、函數聲明

一.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();

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章