1. 程式人生 > >ES6筆記(完整詳細版)

ES6筆記(完整詳細版)

第三階段 企業級開發

第一章 環境搭建

一、Node介紹

    Node也叫NodeJS,Node.js,由Ryan-Dahl於2009年5月在GitHub釋出了第一版。
    Node是一個JavaScript執行環境(runtime)。實際上他是對Google V8引擎進行了封裝。
    官網介紹:一個搭建在ChromJavaScript執行時上的平臺,用於構建高速、可伸縮的網路程式。
    Node採用的事件驅動、非阻塞I/O模型,使它既輕量又高效,併成為構建執行在分散式裝置上的資料密集型實時程式的完美選擇。
    目前Node在實際開發中主要作為開發基礎環境存在,用於進行模組管理,編譯es6程式碼,編譯sass檔案,執行各種Js指令碼。

二、Node安裝

1、下載

進入官網http://nodejs.org/en/,選取合適的版本進行下載

2、安裝

Linux:先解將安裝包壓,然後 進行環境變數的配置即可。
windows:直接點選安裝即可。

三、Node使用

1、基本使用

1)執行js指令碼

 $ node demo.js

2)REPL環境

在命令列鍵入node命令,後面沒有檔名,就進入一個Node.js的REPL環境(Read-eval-print loop,"讀取-求值-輸出迴圈"),可以直接執行各種JavaScript命令。

 $ node
 > 1+1
 2
 >

四、模組化結構

1)模組化

  Node.js採用模組化結構,按照CommonJS規範定義和使用模組。在Node中,以模組為單位劃分所有功能,並且提供一個完整的模組載入機制,使得我們可以將應用程式劃分為各個不同的部分,並且對這些部分進行很好的協同管理。通過將各種可重用的程式碼編寫在各種模組中的方法,我們可以大大減少應用程式的程式碼量,提高應用程式開發效率以及應用程式的可讀性。通過模組載入機制,我們也可以將各種第三方模組引入到我們的應用程式中。

2)CommonJS

   JavaScript是一種功能強大的面嚮物件語言,具有一些最快速的動態語言直譯器。官方JavaScript規範定義了一些用於構建基於瀏覽器應用程式的物件的API。但是,規範並沒有定義一個用於對構建更廣泛的應用程式的標準庫。CommonJS API將通過定義處理許多常見應用程式需求的API來填補這一空白,最終提供與Python、Ruby和Java一樣豐富的標準庫。其意圖是應用程式開發人員能夠使用CommonJS API編寫應用程式,然後在不同的JavaScript直譯器和主機環境中執行該程式。在相容CommonJS的系統中,可以使用JavaScript程式開發:
     伺服器端JavaScript應用程式
     命令列工具
     圖形介面應用程式
     混合應用程式(如Titanium或Adobe AIR)
   Node應用由模組組成,採用CommonJS模組規範。

3)模組作用域

    每一個檔案就是一個模組,有自己的作用域。在一個檔案裡面定義的變數、函式、類,都是私有的,對其他檔案不可見。

  私有屬性:

    //example.js
    var x = 5;
    var addX = function(value){
         return value+x;
    };

    上面程式碼中,變數x和函式addX,是當前檔案example.js私有的,其他檔案不可見。

  全域性屬性:

    如果想在多個檔案分享變數,必須定義為global物件的屬性。

    global.warning = true;

4)模組互動

     CommonJS規範規定,每個模組內部,module變數代表當前模組。這個變數是一個物件,它的export屬性(即module.exports)是對外的介面。載入某個模組,其實是載入該模組的module.exports屬性。

   定義模組:

     var x = 5;
     var addX = function(value){
         return value+x;
     };
     module.export.x = x;
     module.export.addX = addX;


   模組載入:

       require方法用於載入模組。
       var example = require('./example.js');
       console.log(example.x); //5
       console.log(example.addX(1)); //6

5)模組物件

   Node內部提供一個Module構建函式。所有模組都是Module的例項。每個模組內部都有一個module物件,代表當前模組。它有以下屬性:

   module.id 模組的識別符,通常是帶有絕對路徑的模組檔名。
   module.filename 模組的檔名,帶有絕對路徑。
   module.loaded 返回一個布林值,表示模組是否已經載入完成。
   module.parent 返回一個物件,表示呼叫該模組的模組。
   module.children 返回一個數組,表示該模組要用到的其他模組。
   module.exports 表示模組對外輸出的值。

6)exports變數

   為了方便,Node為每個模組提供一個exports變數,指向module.exports。這等同於在每個模組頭部,有這樣一行命令:
   var export = module.exports;

五、核心模組

1)path模組

  path模組提供了一些工具函式,用於處理檔案與目錄的路徑,使用如下方法引用: 
  
  var path = require('path');
  path.basename() 該方法返回一個引數路徑的最後一個部分
  path.dirname() 該方法返回一個path的目錄名
  path.extname() 該方法返回path的副檔名,即從path的最後一部分中的最後一個。(句號)字元到字串結束。
  path.isAbsolute() 該方法判定path是否為一個絕對路徑。
  path.join() 該方法使用平臺特定的分隔符把全部給定的path片段連線到一起,並規範化生成的路徑。
  path.nomalize() 該方法會規範化給定的path,並解析'..'和'.'片段。
  path.delimiter  該屬性提供平臺特定的路徑分隔符。

 另外:

   __filename:指向當前執行的指令碼檔名
   __dirname:指向當前執行的指令碼所在目錄

2)querystring模組

   querystring模組提供了一些實用函式,用於解析與格式化URL查詢字串。使用如下方法引用:
   
   var querystring = require('querystring');
   querystring.stringify(obj[,sep[,eq]])  將物件轉換為查詢字串
         obj 要序列化成URL查詢字串的物件。
         sep 用於界定查詢字串中的鍵值對的子字串。預設為'&'。
         eq 用於界定查詢字串中的鍵與值的子字串。預設為'='。
   querystring.parse(str[,sep[,eq]])  將查詢字串轉換為物件。      

3)url模組

   提供了一些實用函式,用於URL處理與解析。可以通過以下方式使用:
   
   var url = require('url');
   url.parse() 將一個url地址轉換為一個物件
   url.resolve() 該方法會以一種Web瀏覽器解析超連結的方式把一個目標URL解析成相對於一個基礎URL
        url.resolve('/one/two/three','four'); // '/one/two/four'
        url.resolve('http://example.com/','/one'); //'http://example.com/one'
        url.resolve('http://example.com/one','/two'); //'http://example.com/two'
        (替換最後一個)

六、npm

1)介紹

npm是Js開發者能夠更方便的分享和服用以及更新程式碼,被複用的程式碼被稱為包或者模組,一個模組中包含了一到多個js檔案。
在模組中一般還會包含一個package.json的檔案,該檔案中包含了該模組的配置資訊。一個完整的專案需要依賴多個模組,

一個完整的npm包含三部分:
    npm網站---用於預覽npm管理的包
    註冊機制---用於上傳包,使用資料庫來維護包與上傳者的資訊
    客戶端---用於安裝包

2)建立一個模組

    Node.js的模組是一種能夠被髮布到npm上的包。
    建立模組從建立package.json檔案開始,package.json是模組的配置檔案。

       可以使用npm init命令來初始化package.json檔案。
         $ npm init
             name 模組名稱           version  模組版本
             description 描述資訊    main   指定模組入口檔案
             Dependencies 依賴關係   engines  指定node版本
             devDependencies 環境依賴或測試依賴
             optionalDependencies 可選擇依賴
             script 定義當前模組指令碼,使用npm run來執行所定義的指令碼  

       使用-y引數建立預設package.json檔案。
         $ npm init -y  

3)安裝npm

  npm會隨著Node一起被安裝到本地。可以使用以下命令來更新npm:

  $ npm install [email protected] -g

  安裝淘寶映象:

      由於預設npm的倉庫在國外,下載起來很慢,可以使用淘寶映象來加快下載速度。

    $ npm install -g cnpm --register=https://register.npm.taobao.org

      Registry註冊中心:
        官方:https://registry.npmjs.org
        淘寶:https://registry.npm.taobao.org
        私有:http://localIP:port  

  修改npm許可權:

     執行npm的時候有時會遇到許可權不足的情況,可以通過以下方式進行修正:

     $ npm config get prefix
     $ sudo chown -R $(whoami) $(npm config get prefix)/{lib/node_modules,bin,share}

4)模組安裝

   如果想要僅在當前模組中使用某個第三方模組,就可以使用npm install的預設安裝,預設安裝即是本地安裝;如果想要在命令列中使用模組,就需要進行全域性安裝。安裝時,如果當前目錄中沒有node_modules目錄,npm就會建立一個該目錄。
   $ npm install <package_name>
       $ npm install 
          安裝所有專案依賴的模組,依賴的模組定義在package.json中
       $ npm install
           安裝模組時,預設會將所安裝的模組寫入到package.json中的dependencies屬性,通過新增一些引數改變這個特性。 

           -S,--save
           -D,--save-dev:Package will appear in your devDenpendencies.
           -O,--save-optional:Package will appear in your optionalDependencies.
           --no--save:Prevents saving to dependencies.
           -E,--save-exact:Saved dependencies will be configured with an exact version rather than using npm's default semver range operator.   

5)模組更新

   全域性更新以來的模組。

   $ npm update <modules_name>

6)模組刪除

    從node_modules中刪除不需要的模組。

    $ npm uninstall -g <package_name>

    不僅刪除node_modules中的依賴,還需要刪除package.json中的資訊,可以使用--save引數。

    $ npm uninstal --save -g <package_name>

7)搭建本地npm倉庫(sinopia)

   安裝  $ npm install -g sinopia
   配置  $ npm set registry http://localhost:4873/
   新增使用者  $ npm adduser --registry http://localhost:4873/
   釋出模組  $ npm publish <module_name>
   啟動  $ sinopia

七、Babel

1、命令列轉碼babel-cli

全域性環境下進行Babel轉碼。這意味著,如果專案要執行,全域性環境必須有Babel,也就是說專案產生了對環境的依賴。

1)安裝

  $ npm install --global babel-cli

2)安裝預設並且新增配置檔案配置.babelrc 
   在當前專案的根目錄下建立該檔案。

   $ npm install --save-dev babel-preset-es2015
   {"presets":["es2015"]}

3)使用

   轉碼結果輸出到標準輸出
   $ babel example.js
   轉碼結果寫入一個檔案,--out-file或-o引數指定輸出檔案
   $ babel example.js --out-file compiled.js
   整個目錄轉碼--out-dir或-d引數指定輸出目錄
   $ babel src --out-dir lib   

2、配置檔案

Babel的配置檔案是.babelrc,存放在專案的根目錄下。
使用Babel的第一步,就是配置這個檔案。該檔案用來設定轉碼規則和外掛,基本格式如下:
{"presets":[],"plugins":[]}

1)presets欄位設定轉碼規則,官方提供以下的規則集,你可以根據需要安裝。

  ES2015轉碼規則
  $ npm install --save-dev babel-preset-es2015  =>es2015
  最新轉碼規則
  $ npm install --save-dev babel-preset-latest  =>latest
  不會過時的轉碼規則
  $ npm install --save-dev babel-preset-nev  =>env

2)然後,將這些規則加入.babelrc。

  {"presets":["es2015"],"plugins":[],"ignore":[]}

3、將babel-cli安裝到專案中

1)安裝babel-cli及預設

  $ npm install --save-dev babel-cli
  $ npm install --save-dev babel-preset-env

2)配置檔案.babelrc

   $ vim .babelrc
       {"presets":["env"]}    

3)在package.json中新增指令碼

   {
      //...
      "script":{"build":"babel src -d lib"}
   }          

4)執行指令碼,執行轉碼操作

   $ npm run build

八、babel-polyfill

 Babel預設只轉換新的JavaScript句法(syntax),而不轉換新的API,比如Iterator、Generator、Set、Maps、Proxy、Reflect、Symbol、Promise等全域性物件,以及一些定義在全域性物件上的方法(Object.assign)都不會轉碼。
 舉例來說,ES6在Array物件上新增了Array.from方法。Babel就不會轉碼這個方法。如果想讓這個方法執行,必須使用babel-polyfill,為當前環境提供一個墊片。

1、安裝

  $ npm install -save babel-polyfill

2、在js檔案中引用並且使用

  import "babel-polyfill";//或者require("babel-polyfill");

第二章 基礎知識

一、let命令

1.基本用法

ES6新增了let命令,用來宣告變數。用法類似於var,但是也存在新特性:

1)let所宣告的變數,只在let命令所在的程式碼塊有效。適用於for迴圈。

   {let a = 10;var b = 1;}
   //a  a is not defined
   //b  1

2)不存在變數提升。

   console.log{bar};
   let bar = 2;
   //報錯ReferenceError:bar is not defined

3)暫時性死區。

   在程式碼塊內,使用let命令變數之前,該變數都是不可用的。

   var tmp = 123;
   if(true){
        tmp = 'abc'; //ReferenceError
        let tmp;
   }     

4)不允許重複宣告。

    function(){let a =10;let a = 1;} //報錯

2.塊級作用域

 let實際上為JavaScript新增了塊級作用域。外層作用域無法讀取內層作用域的變數,內層作用域可以定義外層作用域的同名變數。

 let foo = 1;
 {
    let foo = 2; //定義同名變數
    let bar = "one";
 }      
 console.log{foo}; //1
 console.log(bar); //報錯

 塊級作用域的出現,實際上使得廣泛應用的立即執行函式表示式(IIFE)不再必要了。    

二、const命令

1. 基本用法

const宣告一個只讀的變數。一旦宣告,常量的值就不能改變。

contst PI = 3.1415; PI = 3; //TypeError:Assignment to constant variable.

1)const宣告的變數不得改變值,這意味著,const一旦宣告變數,就必須立即初始化,不能留到以後賦值。

 const foo; //SyntaxErroe:Missing initializer in const declaration

2)塊級作用域

  只在宣告所在的塊級作用域內有效。

3)暫時性死區

  在程式碼塊內,使用let命令宣告變數之前,該變數是不可用。

  if(true){
     console.log(MAX); //ReferenceError const MAX = 5;
  }

4)不允許重複宣告

   var message = 'Hello!';
   let age = 25;
   //以下兩行都會報錯
   const message = 'Goodbye!';
   const age = 30;

三、解構賦值

1.解構

 ES6允許按照一定模式,從陣列和物件中提取值,對變數進行賦值,這被稱為解構(Destructing)。例如:let [a,b,c] = [1,2,3];
 本質上,這種寫法屬於“模式匹配”,只要等號兩邊的模式相同,左邊的變數就會被賦予對應的值。
 如果結構不成功,變數的值就等於undefined。另一種情況是不完全解構,即等號左邊的模式只匹配一部分的等號右邊的陣列。這種情況下,解構依然可以成功。

2.陣列的解構賦值

let [a,b,c] = [1,2,3];

1)不完全解構:

  let [a,[b],d] = [1,[2,3],4]; //a=1;b=2;d=4

2)集合解構:

  let[head,...tail] = [1,2,3,4]; //head=1;tail=[2,3,4]

3)預設值(當匹配值嚴格等於undefined時,預設值生效):

  let [x,y='b'] = ['a']; //x = 'a',y = 'b'

4)預設值也可以為函式:

  function f(){ console.log('aaa'); }
  let [x=f()] = [1];

3.物件的解構賦值

 1)物件的屬性沒有次序,變數必須與屬性同名,才能取到正確的值

    let {foo,bar} = {foo:'aaa',bar:'bbb'}; //foo='aaa';bar='bbb'

 2)如果變數名與屬性名不一致,必須寫成下面這樣。

     var {foo:baz} = {foo:'aaa',bar:'bbb'}; // baz='aaa'    

 3)這實際上說明,物件的解構賦值是下面形式的簡寫。

     let {foo:foo,bar:bar} = {foo:'aaa',bar:'bbb'};

 4)巢狀結構

     let obj = {p:['Hello',{y:'World'}]};
     let {p:[x,{y}]} = obj;  // x='Hello';y='World'  

 5)預設值(預設值生效的條件是,物件的屬性值嚴格等於undefined)

     var {x:y=3} = {}; // y=3

4.字串的解構賦值

 1)解構時,字串被轉換成了一個類似陣列的物件。

   const [a,b,c,d,e] = 'hello'; //a=h;b=e;c=l;d=l;e=o

 2)也可以對陣列的屬性解構

   let {length:len} = 'hello'; // len=5 

5.數值和布林值的解構賦值

  解構時,如果等號右邊是數值和布林值,則會先轉為物件。

  let {toString:s} = 123; // s===Number.prototype.toString true
  let {toString:s} = true; // s===Boolean.prototype.toString true

6.函式引數的解構賦值

 1)基本語法

   function add([x,y]){return x+y;}
   add([1,2]);

 2)預設值

   function move({x=0,y=0}){
       return [x,y];
   }  
   move({x:3,y:8}); //[3,8]
   move({x:3}); //[3,0]
   move({}); //[0,0]
   move(); //[0,0]

7.常見用途

 1)交換變數的值

    let x = 1; 
    let y = 2; 
    [x,y] = [y,x];

 2)從函式返回多個值

    function example(){
       return [1,2,3];
    }
    let [a,b,c] = example();   

 3)函式引數的定義
    
    function f([x,y,z]){...}
    f([1,2,3]);

 4)提取JSON資料
 
    let jsonData = {id:42,status:'OK',data:[867,5309]};
    let {id,status,data:number} = jsonData;   

 5)輸入模組的指定方法

    const {SourceMapConsumer,SourceNode} = require("source-map");

 6)函式引數的預設值         

    jQuery.ajax = function(url,{
      async = true,cache = true,global = true,
      beforeSend = function(){},
      complete = function(){},
      //...more config
    }){//...do stuff};

    指定引數的預設值,就避免了在函式體內部再寫var foo = config.foo||'default foo';這樣的語句。

 7)遍歷map結構
 
    var map = new Map();
    map.set('first','hello');
    map.set('second','world');
    for(let [key,value] of map){
       console.log(key + "is" + value);
    }

第三章 物件、函式、陣列的擴充套件

學習目標:
1)屬性簡寫方式
2)方法簡寫方式
3)Object方法的擴充套件
4)函式預設值
5)箭頭函式
6)擴充套件運算子
7)Array.from()
8)Arra.of()
9)陣列例項的find(),findIndex()
10)陣列例項的fill()
11)陣列例項的entries(),keys(),values()
12)陣列例項的includes()

一、物件擴充套件

1.屬性簡寫

ES6允許直接寫入變數和函式,作為物件的屬性和方法。這時,屬性名為變數名,屬性值為變數的值。

   var foo = 'bar';
   var baz = {foo};
   =>
   var baz = {foo:foo}; 

2.方法簡寫

   var o = {method(){return "hello!";}};
   =>
   var o = {method:function(){return "hello!";}};   

3.屬性名錶達式

 ES6允許字面量定義物件時,可以把表示式放在方括號內。

   let propKey = 'foo';
   let obj = {[propKey]:true,['a'+'bc']:123};

4.方法的name屬性

 函式的name屬性,返回函式名。

   const person = {sayName(){console.log('hello!');}};
   person.sayName.name; //"sayName" 

5.Object.is(value1,value2)

 同值相等,與===類似,不同之處在於:+0不等於-0;NaN等於自身
 
 Object.is('foo','foo'); //true
 Object.is({},{}); //false            

二、函式的擴充套件

1.函式引數的預設值

 ES6允許為函式的引數設定預設值,即直接寫在引數定義的後面。

 function log(x,y='World'){
     console.log(x,y);
 }

 通常情況下,定義了預設值的引數,應該是函式的尾引數。
 函式的length屬性,將返回沒有指定預設值的引數個數。

2.與解構賦值預設值結合使用

 引數預設值可以與解構賦值的預設值結合起來使用。

 function foo({x,y=5}){
     console.log(x,y);
 }
 foo({}); // undefined 5
 foo({x:1}); //1  5
 foo({x:1,y:2}); // 1 2

3.rest引數

 ES6引入rest引數(形式為"...變數名"),用於獲取函式的多餘引數,這樣就不需要使用arguments物件了。
 rest引數搭配的變數是一個數組,該變數將多餘的引數放入陣列中。

 function add(...values){
     let sum = 0;
     for(var val of values){
         sum += val;
     }
     return sum;
 }
 add(2,3,5); //10

4.箭頭函式

 ES6允許使用“箭頭”(=>)定義函式

1)基本用法:

 var f = v => v;
 等價於
 var f = function(v){
    return v;
 }

 如果箭頭函式不需要引數或需要多個引數,就使用一個圓括號代表引數部分。
 如果箭頭函式的程式碼塊部分多於一條語句,就要使用大括號將他們括起來,並且使用return語句返回。

 2)this

 箭頭函式裡面沒有自己的this,而是引用外層的this。

 //ES6
 function foo(){
    setTimeout(()=>{
       console.log('id:',this.id);
    },1000);
 }

 //ES5
 function foo(){
    var _this = this;
    setTimeout(function(){
       console.log('id:',_this.id);
    },1000);
 }

 不能作為建構函式,沒有內部屬性arguments。

5.擴充套件運算子

 擴充套件運算子(spread)是三個點(...)。它好比rest引數的逆運算,將一個數組轉為用逗號分隔的引數序列。

 console.log(...[1,2,3]); // 1,2,3

1) 函式的呼叫

  function add(x,y){
     return x+y;
  }
  add(...[1,3]);
  Math.max(...[14,3,77]);

2) 將字串轉換為陣列
  
  [...'hello']   // ['h','e','l','l','o']  

三、陣列的擴充套件

1. Array.from

用於將兩類物件轉為真正的陣列:類似陣列的物件(array-like object)和可遍歷(iteratble)的物件(包括ES6新增的資料結構Set和Map)。

let arrayLike = {'0':'a','1':'b','2':'c',length:3};

//ES6的寫法

let arr2 = Array.from(arrayLike); //['a','b','c']

只要是部署了Iterator介面的資料結構,Array.from都能將其轉為陣列。

Array.from('hello');  //將字串轉換為陣列['h','e','l','l','o'] 
let namesSet = new Set(['a','b']);
Array.from(namesSet);  //['a','b']

2. Array.of()

 用於將一組值轉換為陣列。這個發的主要目的是彌補陣列建構函式Array()的不足。

 Array.of(3,18,8); //[3,18,8]

3.陣列例項的find()和findIndex()

 find()方法用於找出第一個符合條件的陣列成員。
 它的引數是一個回撥函式,所有陣列成員依次執行該回調函式,直到找出第一個返回值為true的成員,然後返回該成員。如果沒有符合條件的成員,則返回undefined。
 find()方法的回撥函式可以接受三個引數,依次為當前的值、當前的位置和原陣列。

 [1,4,-5,10].find((n)=>n<0); //-5

 findIndex()方法與find()方法非常類似,更換第一個符合條件的陣列成員的位置。如果所有成員都不符合條件,則返回-1.

 [1,5,10,15].findIndex(function(value,index,arr){
       return value > 9;
 }); // 2

4.陣列例項的fill()

 fill()方法使用給定的值填充一個數組。

 ['a','b','c'].fill(7); // [7,7,7]
 new Array(3).fill(7); //[7,7,7]

5.陣列例項的entries(),keys()

  這兩個方法用於遍歷陣列。它們都返回一個遍歷器物件,可以用for...of迴圈進行遍歷,唯一的區別是:keys()是對鍵名的遍歷,entries()是對鍵值對的遍歷。

  for(let index of ['a','b'].keys()){
      console.log(index);
  }
  for(let [index,elem] of ['a','b'].entries()){
      console.log(index,elem);
  }

6.陣列例項的includes()

  該方法返回一個布林值,表示某個陣列是否包含給定的值,與字串的includes方法類似。ES6引入了該方法。

  [1,2,3].includes(2);  // true
  [1,2,3].includes(4);  // false
  [1,2,NaN].includes(NaN);  // true

第四章 Set和Map資料結構及Promise

一、Set

1.set例項的建立

 它類似於陣列,但是成員的值都是唯一的,沒有重複的值。set本身是一個建構函式,用來生成set資料結構。

 const s = new Set();
 [2,3,5,4,5,2,2].forEach(x => s.add(x));
 console.log(s);  //2 3 5 4

 Set函式可以接收一個數組(具有iterable介面的其他資料結構)作為引數,用來初始化。

 [...new Set(array)]  //去除陣列的重複成員

2.set例項的屬性和方法

 Set結構的例項有以下屬性:

   1) Set.prototype.constructor:建構函式,預設就是Set函式。
   2) Set.prototype.size:返回Set例項的成員總數。  
 
 Set結構的例項有以下方法:

   1) add(value):新增某個值,返回Set結果本身。
   2) delete(value):刪除某個值,返回一個布林值,表示刪除是否成功。
   3) has(value):返回一個布林值,表示該值是否為Set的成員。
   4) clear():清除所有成員,沒有返回值。
   5) keys():返回鍵名的遍歷器。
   6) values():返回鍵值的遍歷器。
   7) entries():返回鍵值對的遍歷器。
   8) forEach():使用回撥函式遍歷每個成員

二、Map

1.Map例項的屬性和方法

 Map類似於物件,也是鍵值對的集合,但是“鍵”的範圍不限於字串,各型別的值(包括物件)都可以當做鍵。
 也就是說,Object結構提供了“字串--值”的對應,Map結構提供了“值--值”的對應,是一種更完善的Hash結構的實現。如果你需要“鍵值對”的資料結構,Map比Object更合適。

 Map可以接受一個數組作為引數。該陣列的成員是一個個表示鍵值對的陣列。

 const map = new Map([[name,'張三'],['title','Author']]);

 Map結構的例項有以下屬性:

   Set.prototype.size:返回Map例項的成員總數。 

 Map結構的例項有以下方法:

   1) set(key,value):set方法設定鍵名key對應的鍵值為value,然後返回整個Map結構。如果key已經有值,則鍵值會被更新,否則就新生成該鍵。 
   2) get(key):get方法讀取對應的鍵值,如果找不到key,返回false。
   3) has(key):has方法返回一個布林值,表示某個鍵是否在當前Map物件之中。
   4) delete(key):delete方法刪除某個鍵,返回true。如果刪除失敗,返回false。
   5) clear():清除所有成員,沒有返回值。
   6) keys():返回鍵名的遍歷器。 
   7) values():返回鍵值的遍歷器。
   8) entries():返回鍵值對的遍歷器。
   9) forEach():使用回撥函式遍歷每個成員   

三、Interator

1.Interator(遍歷器)的概念

 遍歷器(Iterator)就是這樣一種機制。它是一種介面,為各種不同的資料結構提供統一的訪問機制。任何資料結構只要部署Iterator介面,就可以完成遍歷操作(即依次處理該資料結構的所有成員)。

 Iterator的作用有三個:

    1)為各種資料結構提供一個統一的、簡便的訪問介面。
    2)使得資料結構的成員能夠按某種次序排列。
    3)ES6創造了一種新的遍歷命令for...of迴圈,Iterator介面主要供for...of消費。

 Iterator的遍歷過程是這樣的:

   1)建立一個指標物件,指向當前資料結構的起始位置。也就是說,遍歷器物件本質上就是一個指標物件。
   2)第一次呼叫指標物件的next方法,可以將指標指向資料結構的第一個成員。
   2)第二次呼叫指標物件的next方法,指標就指向資料結構的第二個成員。
   3)不斷呼叫指標物件的next方法,直到它指向資料結構的結束位置。

2.預設Interator介面

 Iterator介面的目的,就是為所有資料結構提供了一種統一的訪問機制,即for...of迴圈。當使用for...of迴圈遍歷某種資料結構時,該迴圈會自動去尋找Iterator介面。一種資料結構只要部署了Iterator介面,我們就稱這種資料結構是“可遍歷的”。可以通過如下方法訪問Iterator物件:

 var iterator = iterObj[Symbol.iterator]();

 原生具備Iterator介面的資料結構如下:

   Array
   Map
   Set
   String
   TypedArray
   函式的arguments物件
   NodeList物件

四、Promise

1.promise介紹

 Promise是非同步程式設計的一種解決方案,比傳統的解決方案(回撥函式和事件)更合理更強大。它由社群最早提出和實現,ES6將其寫進了語言標準,統一了用法,原生提供了Promise物件。

 所謂Promise,簡單說就是一個容器,裡面儲存著某個未來才會結束的事件(通常是一個非同步操作)的結果。從語法上說,Promise是一個物件,它可以獲取非同步操作的訊息。Promise提供統一的API,各種非同步操作都可以用同樣的方法進行處理。

 有了Promise物件,就可以將非同步操作以同步操作的流程表達出來,避免了層層巢狀的回撥函式。此外,Promise物件提供統一的介面,使得控制非同步操作更加容易。

2.基本用法

 Promise建構函式接收一個函式作為引數,該函式的兩個引數分別是reolve和reject。它們是兩個函式,有JavaScript引擎提供,不用自己部署。

 resolve函式的作用是,將Promise物件的狀態從“未完成”變為“成功”(即從Pending變為Resolved),在非同步操作成功時呼叫,並將非同步操作的結果作為引數傳遞出去。

 reject函式的作用是,將Promise物件的狀態從“未完成”變為“失敗”(即從Pending變為Rejected),在非同步操作失敗時呼叫,並將非同步操作的結果作為引數傳遞出去。

 如果呼叫resolve和reject函式時帶有引數,那麼它們的引數會被傳遞給回撥函式。

 Promise例項生成以後,可以呼叫then方法分別指定Resolved狀態和Rejected狀態的回撥函式。

 .then(function(){
        //success
     },
     function(){
        //error
     });

3. promise.prototype.then()

 Promise例項具有then方法,也就是說,then方法是定義在原型物件Promise.prototype上的。它的作用是為Promise例項新增狀態改變時的回撥函式。
 then方法的第一個引數是Resolved狀態的回撥函式,第二個引數(可選)是Rejected狀態的回撥函式。then方法返回的是一個新的Promise例項(注意:不是原來的Promise例項)。
 因此可以採用鏈式寫法,即then方法後面再呼叫另一個then方法。如果使用兩個then方法,第一個回撥函式完成以後,會將返回結果作為引數,傳入第二個回撥函式。

4. promise.prototype.catch()

 Promise.prototype.catch方法是.then(null,rejection)的別名,用於指定發生錯誤時的回撥函式。
 一般來說,不要在then方法裡面定義Reject狀態的回撥函式(即then的第二個引數),總是使用catch方法。

 Promise物件的錯誤具有“冒泡”性質,會一直向後傳遞,直到被捕獲為止。也就是說,錯誤總是會被下一個catch語句捕獲。

5. promise.all()

 Promise.all方法用於將多個Promise例項包裝成一個新的Promise例項。

 var p = Promise.all([p1,p2,p3]);

 上面程式碼中,Promise.all方法接收一個數組作為引數,p1,p2,p3都是Promise例項,p的狀態由p1,p2,p3決定,分成兩種情況:
 1)只有p1,p2,p3的狀態都變成fulfilled,p的狀態才會變成fulfilled,此時p1,p2,p3的返回值組成一個數組,傳遞給p的回撥函式。
 2)只要p1,p2,p3之中有一個被rejected,p的狀態就會變成rejectedd,此時第一個被rejected的例項的返回值,會傳遞給p的回撥函式。

6. promise.race()

 promise.race方法同樣是將多個Promise例項包裝成一個新的Promise。下面程式碼中,只要p1,p2,p3之中有一個例項率先改變狀態,p的狀態就跟著改變。那個率先改變的Promise例項的返回值,就傳遞給p的回撥函式。

 var p = Promise.race([p1,p2,p3]);

7. promise.resolve()

 promise.resolve方法將現有物件轉為Promise物件,例如:
 var jsPromise = promise.resolve($.ajax('/whatever.json'));

 1) 引數是一個Promise例項
    promise.resolve將不做任何修改、原封不動地返回這個例項。
 2) 引數是一個thenable物件
    thenable物件指的是具有then方法的物件,promise.resolve方法會將這個物件轉為Promise物件,然後立即執行thenable物件的then方法。
 3) 引數不是具有then方法的物件,或根本就不是物件
    如果引數是一個原始值,或者是一個不具有then方法的物件,則promise.resolve方法返回一個新的Promise物件,狀態為resolved。
 4) 不帶有任何引數
    直接返回一個resolve狀態的Promise物件。需要注意的是,立即resolve的Promise物件,是在本輪“事件迴圈”(event loop)的結束時,而不是在下一輪的“事件迴圈”的開始時。  

8. promise.reject()

 promise.reject方法也會返回一個新的Promise例項,該例項的狀態為rejected。

 var p = promise.reject("出錯了");
 =>
 var p = new Promise((resolve,reject)=>reject('出錯了'));

9. finally()

 finally方法用於指定不管Promise物件最後狀態如何都會執行的操作。它接收一個普通的回撥函式作為引數,該函式不管怎樣都必須執行。

 下面是一個例子,伺服器使用Promise處理請求,然後使用finally方法關掉伺服器。
 
 server.listen(0).then(function(){
     // run test
 }).finallly(server.stop);

第五章 ES6模組

1、介紹
歷史上,JavaScript一直沒有模組(modules)體系,無法將一個大程式拆分成互相依賴上午小檔案,再用簡單的方法拼接裝起來。
在ES6之前,社群制定了一些模組載入方案,最主要的有CommonJS和AMD兩種。前者用於伺服器,後者用於瀏覽器。
ES6在語言標準的層面上,實現了模組功能,而且實現的相當簡單,完全可以取代CommonJS和AMD規範,成為瀏覽器和伺服器通用的模組解決方案。

2、export命令

模組功能主要由兩個命令構成:export和import。export命令用於規定模組的對外介面,import命令用於輸入其他模組提供的功能。
一個模組就是一個獨立的檔案。該檔案內部的所有變數,外部無法獲取。
如果你希望外部能夠讀取模組內部的某個變數,就必須適應使用export關鍵字輸出該變數。下面是一個JS檔案。裡面使用export命令輸出變數。

var firstName = 'Michael';
var lastName = 'Jackson';
var year = 1958;
function multiply(x,y){return x * y;};
export{firstName,lastName,year,multiply};

需要特別注意的是,export命令規定的是對外的介面,必須與模組內部的變數建立一一對應關係,不能直接匯出一個值。

export var m = 1;
或 var m = 1; export{m};
或 var n = 1; export{n as m};
在一個模組中,export可以呼叫多次。

3、import命令

使用export命令定義了模組的對外介面以後,其他JS檔案就可以通過import命令載入這個模組。

1)解構匯入

  import {firstName,lastName,year} from './profile';

2)重新命名變數

   import {lastName as surname} from './profile';

3)重複匯入

    import {name} from './module1';
    import {age} from './module1';

   如果多次重複執行同一import語句,那麼只會執行一次模組程式碼。
   
4)模組的整體載入
   
    import * as persom from './module1';    

4、export default命令

使用import命令的時候,使用者需要知道所要載入的變數名或函式名,否則無法載入。
但是,使用者肯定希望快速上手,未必願意閱讀文件,去了解模組有哪些屬性和方法。
為了給使用者提供方便,讓他們不用閱讀文件就能載入模組,就要用到export default命令為模組指定預設輸出。

export default function(){
    console,log('foo');
}

其他模組載入該模組時,import命令可以為該匿名函式指定任意名字。

import customName from './export-default';
customName(); //'foo'

export default命令用於指定模組的預設輸出。顯然,一個模組只能有一個預設輸出,因此export default命令只能使用一次。所以,import命令後面才不用加大括號,因為只可能對應一個方法或者物件。

5、export與import的複合寫法

如果在一個模組之中,先輸入後輸出同一個模組,import語句可以與export語句寫在一起。

export {foo,bar} from './my_module';
=>
import {foo,bar} from 'my_module';
export {foo,bar};

第六章 Class

1、介紹

JavaScript語言中,生成例項物件的傳統方法是通過建構函式ES6提供了更接近傳統語言的寫法,引入了class(類)概念,作為物件的模板。通過class關鍵字,可以定義類。
基本上,ES6的class可以看作只是一個語法糖,它的絕大部分功能,ES5都可以做到,新的class寫法只是讓物件原型的寫法更加清晰更像面向物件程式設計的語法而已。所以ES6的類,完全可以看作建構函式的另一種寫法。

class Point{
constructor(x,y){
this.x = x;
this.y = y;
}
toString(){
return ‘(’ + this.x + ‘,’ + this.y + ‘)’;
}
}

2、方法

在類中可以直接定義方法,實際上類的所有方法都定義在類的prototype屬性上面。在類的例項上面呼叫方法,其實就是呼叫原型上的方法。

class Point{
  constructor(){//...}
  toString(){//...}
  toValue(){//...}
}

由於類的方法都定義在prototype物件上面,所以類的新方法可以新增在prototype物件上面。Object.assign方法可以很方便地向類新增多個方法。

class Point{
   constructor(){//...}
}
Object.assign(Point.prototype,{
  toString(){},
  toValue(){}
});

3、constructor方法

constructor方法是類的預設方法,通過new命令生成物件例項時,自動呼叫該方法。一個類必須有constructor方法,如果沒有顯示定義,一個空的constructor方法會被預設新增。

class Point{}
=>
class Point {costructor(){}}

類必須使用new呼叫,否則會報錯。這是它跟普通建構函式的一個主要區別,後者不用new也可以執行。

4、靜態方法

類相當於例項的原型,所有在類中定義的方法,都會被例項繼承。如果在一個方法前,加上static關鍵字,就表示該方法不會被例項繼承,而是直接通過類來直接呼叫,這就稱為“靜態方法”。

class Foo{
   static classMethod(){return 'hello';}
}
Foo.classMethod(); //'hello'

如果靜態方法包含this關鍵字,這個this值得是類,而不是例項。

5、例項屬性

類的例項屬性可以定義在建構函式中。

class Person{
   constructor(id,name,age){
       this.id = id;
       this.name = name;
       this.age = age;
   }
}

6、靜態屬性

直接在類上定義的屬性是靜態屬性。

class Foo{
   //...
}
Foo.prop = 1;
Foo.prop; //1

目前,只有這種寫法可行,因為ES6明確規定,class內部只有靜態方法,沒有靜態屬性。

7、繼承

class可以通過extends關鍵字實現繼承,這比ES5的通過修改原型鏈實現繼承要清晰和方便很多。

class Animal{
   constructor(name){
      this.name = name;
   }
   sayName(){
      console.log("my name is",this.name);
   }
}
class Dog extends Animal{
    //...
}

子類必須在constructor方法中呼叫super方法,否則新建例項時會報錯。
這是因為子類沒有自己的this物件,而是繼承父類的this物件,然後對其進行加工。
如果不呼叫super方法,子類就得不到this物件。子類建構函式可以省略。
在子類建構函式中,只有呼叫super之後,才可以使用this關鍵字,否則會報錯。

8、super

super這個關鍵字,既可以當做函式使用,也可以當做物件使用。在這兩種情況下,它的用法完全不同。

1)函式

  子類B的建構函式之中的super(),代表呼叫父類的建構函式。super雖然代表了父類A的建構函式,但是返回的是子類B的例項,即super內部的this指的是B,因此super()在這裡相當於A.prototype.constructor.call(this)。

2)物件
    
   在普通方法中,指向弗雷德原型物件;在靜態方法中,指向父類。由於super指向父類的原型物件,所以定義在父類例項上上午方法或屬性,是無法通過super呼叫的。

   ES6規定,通過super呼叫父類的方法時,super會繫結子類的this。

   super.print();
   =>
   super.print.call(this);

   不能直接列印super,因為無法得知super到底是函式還是物件。

9、類的prototype屬性和__proto__屬性

 class作為建構函式的語法糖,同時有prototype屬性和__proto__屬性,因此同時存在兩條繼承鏈:
 1)子類的__proto__屬性,表示建構函式的繼承,總是指向父類。
 2)子類prototype屬性的__proto__屬性,表示方法的繼承,總是指向父類的prototype屬性。

 class A {}
 class B extends A {}
 B.__proto__ === A; //true
 B.prototype.__proto__ === A.prototype; //true

 類的繼承是按照下面的模式實現的。

 class A {}
 class B {}
  //B的例項繼承A的例項
 Object.setPrototypeOf(B.prototype,A.prototype);
  //B的例項繼承A的靜態屬性
 Object.setPrototypeOf(B,A);
 const b = new B();