ES6學習總結

林Clown發表於2017-07-11

  1、ES6的產生

  JavaScript 是一個持續演進的程式語言,並由瀏覽器廠商、開發者和社群成員組成的委員會制定標準。委員會引入了JavaScript歷史上的最大更新 ES6 (ES2016),而 ES7 是年度更新的第一版(ES2017)。

  2、ES6ES7優點

    • 簡化常見程式設計模式

    • 使程式碼更容易編寫以及定製底層的 JavaScript 行為。

  3、新特性

    • Class 可以讓開發者更容易地編寫物件導向程式,並安全地擴充套件extend JavaScript內建物件。

    • 箭頭函式Arrow functions、預設引數default parameters和陣列的遍歷方法array convenience methods使得常用功能更容易編寫,而不需要在專案之間複製黏貼程式碼。

    • JavaScript 的非同步處理流和巢狀回撥讓人難以理解,所以 ES6 中引入了promises 、迭代器和生成器iterators, and generators以簡化非同步程式碼,讓控制流更直觀而不易出錯。

  4、ES6基礎---let命令

    • 用於宣告(區域性)變數,類似於var(全域性),但是宣告的變數只在let命令所在的程式碼塊有效。
    • 建立塊級作用域(下面詳細介紹)

      1 if(true){
      2     let a=10;
      3     var b=1;
      4 }
      5 console.log(a);     //Uncaught ReferenceError: a is not defined
      6 console.log(b);        //1
    • 沒有變數提升

      1 console.log(typeof str);     //undefined
      2 var str = 10;     
      3 
      4 console.log(typeof abc);     //Uncaught ReferenceError: abc is not defined
      5 let abc = 10;
    • 不允許重複宣告

      1 let a=10;
      2 let a=20;
      3 console.log(a);        //Uncaught SyntaxError: Identifier 'b' has already been declared
      4 
      5 var a=10;
      6 var a=20;
      7 console.log(a);        //20
    • 暫時性死區

      在塊級作用域中使用let宣告變數時,塊級作用域會形成一個封閉的環境,不能訪問外部宣告的變數

      1 var c = 19;
      2 if(true) {
      3       console.log(c);
      4       let c;     //Uncaught ReferenceError: c is not defined
      5 }

 

  5、為什麼需要塊級作用域?

  ES5 只有全域性作用域和函式作用域,沒有塊級作用域,這帶來很多不合理的場景。

    • 第一種場景----內層變數可能會覆蓋外層變數。
      1 var tmp = 'hi';
      2 function f() {
      3      console.log(tmp);        //這裡想使用外層的tmp='hi'
      4      if (false) {            //if程式碼塊之內使用內層的tmp='hello'
      5         var tmp = 'hello';
      6      }
      7 }
      8 f();     //但是輸出結果是undefined,因為這裡存在變數提升,導致內部tmp覆蓋了外層的tmp
    • 第二種場景----用來計數的迴圈變數洩露為全域性變數。
      1 var s = 'hello';
      2 for (let i = 0; i < s.length; i++) {
      3       console.log(s[i]);
      4 }
      5 console.log(i);     // 5  
      6 //上面程式碼中,變數i只用來控制迴圈,但是迴圈結束後,它並沒有消失,洩露成了全域性變數

  6、ES6基礎---const命令

  const宣告一個只讀的常量

    • 旦宣告,常量的值就不能改變

      1 const PI = 3.1415;        // 3.1415
      2 PI = 3;                    // TypeError: Assignment to constant variable.
    • 只宣告,不賦值,也會報錯

      1 const a;        // SyntaxError: Missing initializer in const declaration
    • let相同,只在宣告的塊級作用域中有效

      1 if (true) {
      2      const a = 5;
      3 }
      4 console.log(a);        // Uncaught ReferenceError: a is not defined
    • 不允許重複宣告
      1 var message = "Hello!";
      2 let age = 25;
      3 
      4 // 以下兩行都會報錯
      5 const message = "Goodbye!";
      6 const age = 30;
    • 也沒有變數提升,存在暫時性死區
      1 if (true) {
      2      console.log(a);     //Uncaught ReferenceError: a is not defined
      3      const a = 5;
      4 }

  7、ES6 宣告變數的六種方法

    • ES5 只有兩種宣告變數的方法:var命令和function命令。
    • ES6除了新增letconst命令,另外兩種宣告變數的方法:import命令和class命令。所以,ES6 一共有6種宣告變數的方法。

  8、箭頭函式

    • ES6 允許使用“箭頭”(=>)定義函式。
      1 var f = v => v;
    • 上面箭頭函式等同於
      1 var f = function(v) {  return v;};
    • 引入箭頭函式的兩個作用
      • 更簡短的函式書寫,簡化回撥函式
        1 // 正常函式寫法
        2 [1,2,3].map(function (x) {
        3   return x * x;
        4 });
        5 // 箭頭函式寫法
        6 [1,2,3].map(x => x * x);
      • 對this的詞法解析,自己內部沒有this
    • 使用時需注意
      • 函式體內的this物件,就是定義時所在的物件,而不是使用時所在的物件。
      • 不可以當作建構函式,也就是說,不可以使用new命令,否則會丟擲一個錯誤。
      • 不可以使用arguments物件,該物件在函式體內不存在。如果要用,可以用 rest 引數代替。
      • 不可以使用yield命令,因此箭頭函式不能用作 Generator 函式
        1 //this物件的指向是可變的,但是在箭頭函式中,它是固定的。
        2 function foo() {
        3   setTimeout(() => {
        4     console.log('id:', this.id);
        5   }, 100);
        6 }
        7 var id = 21;
        8 foo.call({ id: 42 });       // id: 42
        9 //箭頭函式可以讓setTimeout裡面的this,繫結定義時所在的作用域,而不是指向執行時所在的作用域
    • 小測試:請問下面的程式碼中有幾個this?
       1 function foo() {
       2   return () => {
       3     return () => {
       4       return () => {
       5         console.log('id:', this.id);
       6       };
       7     };
       8   };
       9 }
      10 
      11 var f = foo.call({id: 1});
      12 
      13 var t1 = f.call({id: 2})()();     // id: 1
      14 var t2 = f().call({id: 3})();     // id: 1
      15 var t3 = f()().call({id: 4});     // id: 1

       

  上面程式碼之中,只有一個this,就是函式foothis,所以t1t2t3都輸出同樣的結果。因為所有的內層函式都是箭頭函式,都沒有自己的this,它們的this其實都是最外層foo函式的this

  除了this,以下三個變數在箭頭函式之中也是不存在的,指向外層函式的對應變數:argumentssupernew.target

  9、promise物件

    • 非同步程式設計的一種解決方案
      • 避免了層層巢狀的回撥函式,使用鏈式呼叫方式來組織程式碼,更加直觀
      • 提供統一的介面,使得控制非同步操作更加容易。
    • 三種狀態
      • Pending進行中
      • Resolved已完成(又稱Fulfilled)
      • Rejected已失敗
    • 基本用法
       1 //創造一個promise例項
       2 var promise = new Promise(function(resolve, reject) {
       3   // ... some code
       4 
       5   if (/* 非同步操作成功 */){
       6     resolve(value);
       7   } else {
       8     reject(error);
       9   }
      10 });
      11 //例項生成後,可以用then方法分別指定Resolved狀態和Reject狀態的回撥函式。
      12 promise.then(function(value) {
      13   // success
      14 }, function(error) {
      15   // failure
      16 });
    • promise.all()
      • 用於將多個 Promise 例項,包裝成一個新的 Promise 例項。

        var p = Promise.all([p1, p2, p3]);
      • p的狀態由p1p2p3決定,分成兩種情況。

        (1)只有p1p2p3的狀態都變成fulfilledp的狀態才會變成fulfilled,此時p1p2p3的返回值組成一個陣列,傳遞給p的回撥函式。

        (2)只要p1p2p3之中有一個被`rejected`,p的狀態就變成rejected,此時第一個被reject的例項的返回值,會傳遞給p的回撥函式。

         1 // 生成一個Promise物件的陣列
         2 var promises = [2, 3, 5, 7, 11, 13].map(function (id) {
         3   return getJSON('/post/' + id + ".json");
         4 });
         56 Promise.all(promises).then(function (posts) {
         7   // ...
         8 }).catch(function(reason){
         9   // ...
        10 });
        //上面程式碼中,promises是包含6個 Promise 例項的陣列,只有這6個例項的狀態都變成fulfilled,或者其中有一個變為rejected,才會呼叫Promise.all方法後面的回撥函式

 

  先到這裡吧!有什麼不對的地方望大家多多指點!