Node.js從入門到實戰ECMAScript6一頁紙總結(很大的一頁紙)

1、ES5/ES6和babelnode

ECMAScript5,即ES5,是ECMAScript的第五次修訂,於2009年完成標準化,如今的瀏覽器已經至關於徹底實現了這個標準。
ECMAScript6,即ES6,也稱ES2015,是ECMAScript的第六次修訂,於2015年完成,而且運用的範圍逐漸開始擴大,由於其相對於ES5更加簡潔,提升了開發速率,開發者也都在陸續進行使用,可是因爲ES6還存在一些支持的問題,因此通常即便是使用ES6開發的工程,也須要使用Babel進行轉換。
Babel是一個普遍使用的ES6轉碼器,能夠將ES6代碼轉爲ES5代碼,從而在現有環境執行。這一過程叫作「源碼到源碼」編譯, 也被稱爲轉換編譯。es6

通常來講Babel做爲依賴包被引入ES6工程中,此處再也不介紹以cli方式使用的ES6,若是你須要以編程的方式來使用 Babel,可使用 babel-core 這個包。babel-core 的做用是把 js 代碼分析成 ast ,方便各個插件分析語法進行相應的處理。有些新語法在低版本 js 中是不存在的,如箭頭函數,rest 參數,函數默認值等,這種語言層面的不兼容只能經過將代碼轉爲 ast,分析其語法後再轉爲低版本 js。babel的使用過程以下:npm

1. 首先安裝 babel-core。

 

$ npm install babel-core

2. 在文件開頭引入babel:

 

var babel = require("babel-core");

3. 文件轉換

字符串形式的 JavaScript 代碼能夠直接使用 babel.transform 來編譯。
編程

  1.  
    babel.transform( "code();", options);
  2.  
    // => { code, map, ast }

若是是文件的話,可使用異步 api:json

  1.  
    babel.transformFile( "filename.js", options, function(err, result) {
  2.  
    result; // => { code, map, ast }
  3.  
    });

或者是同步 api:api

  1.  
    babel.transformFileSync( "filename.js", options);
  2.  
    // => { code, map, ast }

或者在development環境下可使用bable-node和bable-register的方式配置,過程以下:數組

1. 添加依賴

 

在Node.js工程package.json包中添加以下依賴:瀏覽器

 

  1.  
    "devDependencies": {
  2.  
    "babel-cli": "^6.26.0",
  3.  
    "babel-eslint": "^8.0.1",
  4.  
    "babel-plugin-transform-flow-strip-types": "^6.22.0",
  5.  
    "babel-preset-es2015": "^6.24.1",
  6.  
    "babel-register": "^6.26.0",
  7.  
    ...
  8.  
    }

 

2. 配置dev腳本服務器

 

  1.  
    "scripts": {
  2.  
    "serve-dev": "NODE_ENV=development nodemon ./src/index.js --exec babel-node",
  3.  
    },

接下來羅列一下ES6的語法要點參考備用。babel

 

2、let, const

這兩個的用途與var相似,都是用來聲明變量的,但在實際運用中他倆都有各自的特殊用途。
首先來看下面這個例子:

  1.  
    var name = 'zach'
  2.  
     
  3.  
    while ( true) {
  4.  
    var name = 'obama'
  5.  
    console.log(name) //obama
  6.  
    break
  7.  
    }
  8.  
     
  9.  
    console.log(name) //obama

使用var 兩次輸出都是obama,這是由於ES5只有全局做用域和函數做用域,沒有塊級做用域,這帶來不少不合理的場景。第一種場景就是你如今看到的內層變量覆蓋外層變量。而let則實際上爲JavaScript新增了塊級做用域。用它所聲明的變量,只在let命令所在的代碼塊內有效。

 

 

  1.  
    let name = 'zach'
  2.  
     
  3.  
    while ( true) {
  4.  
    let name = 'obama'
  5.  
    console.log(name) //obama
  6.  
    break
  7.  
    }
  8.  
     
  9.  
    console.log(name) //zach

另一個var帶來的不合理場景就是用來計數的循環變量泄露爲全局變量,看下面的例子:

 

  1.  
    var a = [];
  2.  
    for ( var i = 0; i < 10; i++) {
  3.  
    a[i] = function () {
  4.  
    console.log(i);
  5.  
    };
  6.  
    }
  7.  
    a[ 6](); // 10

上面代碼中,變量i是var聲明的,在全局範圍內都有效。因此每一次循環,新的i值都會覆蓋舊值,致使最後輸出的是最後一輪的i的值。而使用let則不會出現這個問題。

  1.  
    var a = [];
  2.  
    for ( let i = 0; i < 10; i++) {
  3.  
    a[i] = function () {
  4.  
    console.log(i);
  5.  
    };
  6.  
    }
  7.  
    a[ 6](); // 6

const也用來聲明變量,可是聲明的是常量。一旦聲明,常量的值就不能改變。

  1.  
    const PI = Math.PI
  2.  
     
  3.  
    PI = 23 //Module build failed: SyntaxError: /es6/app.js: "PI" is read-only

 

3、解構賦值

 

ES6 容許按照必定模式,從數組和對象中提取值,對變量進行賦值,這被稱爲解構(Destructuring)。

  1.  
    let [a, b, c] = [ 1, 2, 3];
  2.  
    let [foo, [[bar], baz]] = [ 1, [[ 2], 3]];
  3.  
    foo // 1
  4.  
    bar // 2
  5.  
    baz // 3
  6.  
    let [ , , third] = [ "foo", "bar", "baz"];
  7.  
    third // "baz"
  8.  
    let [head, ...tail] = [ 1, 2, 3, 4];
  9.  
    head // 1
  10.  
    tail // [2, 3, 4]

解構不成功時變量賦值爲undefined

  1.  
    let [x, y, ...z] = [ 'a'];
  2.  
    x // "a"
  3.  
    y // undefined
  4.  
    z // []

存在不徹底解構的狀況以下:

 

  1.  
    let [x, y] = [ 1, 2, 3];
  2.  
    x // 1
  3.  
    y // 2
  4.  
     
  5.  
    let [a, [b], d] = [ 1, [ 2, 3], 4];
  6.  
    a // 1
  7.  
    b // 2
  8.  
    d // 4

解構賦值容許指定默認值。ES6 內部使用嚴格相等運算符(===),判斷一個位置是否有值。因此,只有當一個數組成員嚴格等於undefined,默認值纔會生效。

 

  1.  
    let [x = 1] = [ undefined];
  2.  
    x // 1
  3.  
     
  4.  
    let [x = 1] = [ null];
  5.  
    x // null

以下是解構賦值的應用實例:

 

  1.  
    let { foo: baz } = { foo: 'aaa', bar: 'bbb' };
  2.  
    baz // "aaa"
  3.  
     
  4.  
    let obj = { first: 'hello', last: 'world' };
  5.  
    let { first: f, last: l } = obj;
  6.  
    f // 'hello'
  7.  
    l // 'world'
  8.  
     
  9.  
    let arr = [ 1, 2, 3];
  10.  
    let { 0 : first, [arr.length - 1] : last} = arr; //屬性名錶達式
  11.  
    first // 1
  12.  
    last // 3
  13.  
     
  14.  
    function add([x, y]){
  15.  
    return x + y;
  16.  
    }
  17.  
     
  18.  
    add([ 1, 2]); // 3 //函數入參也可進行解構
  19.  
     
  20.  
    // 返回一個數組
  21.  
     
  22.  
    function example() {
  23.  
    return [ 1, 2, 3];
  24.  
    }
  25.  
    let [a, b, c] = example();
  26.  
     
  27.  
    let jsonData = {
  28.  
    id: 42,
  29.  
    status: "OK",
  30.  
    data: [ 867, 5309]
  31.  
    };
  32.  
     
  33.  
    let { id, status, data: number } = jsonData;
  34.  
     
  35.  
    // 獲取鍵名
  36.  
    for ( let [key] of map) {
  37.  
    // ...
  38.  
    }
  39.  
     
  40.  
    // 獲取鍵值
  41.  
    for ( let [,value] of map) {
  42.  
    // ...
  43.  
    }

以下兩種函數的定義方法在解構賦值時具有不一樣的返回值:

 

  1.  
    function m1({x = 0, y = 0} = {}) {
  2.  
    return [x, y];
  3.  
    }
  4.  
     
  5.  
    // 寫法二
  6.  
    function m2({x, y} = { x: 0, y: 0 }) {
  7.  
    return [x, y];
  8.  
    }
  1.  
    // x 有值,y 無值的狀況
  2.  
    m1({ x: 3}) // [3, 0]
  3.  
    m2({ x: 3}) // [3, undefined]
  4.  
     
  5.  
    // x 和 y 都無值的狀況
  6.  
    m1({}) // [0, 0];
  7.  
    m2({}) // [undefined, undefined]
  8.  
     
  9.  
    m1({ z: 3}) // [0, 0]
  10.  
    m2({ z: 3}) // [undefined, undefined]

4、模板字符串

模板字符串(template string)是加強版的字符串,用反引號(`)標識。它能夠看成普通字符串使用,也能夠用來定義多行字符串,或者在字符串中嵌入變量

  1.  
    // 普通字符串
  2.  
    `In JavaScript '\n' is a line-feed.`
  3.  
     
  4.  
    // 多行字符串
  5.  
    `In JavaScript this is
  6.  
    not legal.`
  7.  
     
  8.  
    console.log( `string text line 1
  9.  
    string text line 2`);
  10.  
     
  11.  
    // 字符串中嵌入變量
  12.  
    let name = "Bob", time = "today";
  13.  
    `Hello ${name}, how are you ${time}?`

若是在模板字符串中須要使用反引號,則前面要用反斜槓轉義。

 

let greeting = `\`Yo\` World!`;

大括號內部能夠放入任意的 JavaScript 表達式,能夠進行運算,以及引用對象屬性。若是大括號中的值不是字符串,將按照通常的規則轉爲字符串。好比,大括號中是一個對象,將默認調用對象的toString方法。

標籤模板

 

模板字符串它能夠緊跟在一個函數名後面,該函數將被調用來處理這個模板字符串。這被稱爲「標籤模板」功能(tagged template)。

  1.  
    let a = 5;
  2.  
    let b = 10;
  3.  
     
  4.  
    tag `Hello ${ a + b } world ${ a * b }`;
  5.  
    // 等同於
  6.  
    tag([ 'Hello ', ' world ', ''], 15, 50);

模板字符串前面有一個標識名tag,它是一個函數。整個表達式的返回值,就是tag函數處理模板字符串後的返回值。函數tag依次會接收到多個參數。

  1.  
    function tag(stringArr, value1, value2){
  2.  
    // ...
  3.  
    }
  4.  
    // 等同於
  5.  
    function tag(stringArr, ...values){
  6.  
    // ...
  7.  
    }

tag函數的第一個參數是一個數組,該數組的成員是模板字符串中那些沒有變量替換的部分,也就是說,變量替換隻發生在數組的第一個成員與第二個成員之間、第二個成員與第三個成員之間,以此類推。tag函數的其餘參數,都是模板字符串各個變量被替換後的值。因爲本例中,模板字符串含有兩個變量,所以tag會接受到value1和value2兩個參數。也就是說,tag函數實際上如下面的形式調用。

tag(['Hello ', ' world ', ''], 15, 50)

5、rest參數

 

ES6 引入 rest 參數(形式爲...變量名),用於獲取函數的多餘參數,這樣就不須要使用arguments對象了。rest 參數搭配的變量是一個數組,該變量將多餘的參數放入數組中。

  1.  
    // arguments變量的寫法
  2.  
    function sortNumbers() {
  3.  
    return Array.prototype.slice.call( arguments).sort();
  4.  
    }
  5.  
     
  6.  
    // rest參數的寫法
  7.  
    const sortNumbers = (...numbers) => numbers.sort();

arguments對象不是數組,而是一個相似數組的對象。因此爲了使用數組的方法,必須使用Array.prototype.slice.call先將其轉爲數組。rest 參數就不存在這個問題,它就是一個真正的數組,數組特有的方法均可以使用。

 

擴展運算符

rest函數的實現也是基於擴展運算符,擴展運算符(spread)是三個點(...)。它比如 rest 參數的逆運算,將一個數組轉爲用逗號分隔的參數序列。

 

 

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

擴展運算符提供了複製數組的簡便寫法。

 

  1.  
    const a1 = [ 1, 2];
  2.  
    // 寫法一
  3.  
    const a2 = [...a1];
  4.  
    // 寫法二
  5.  
    const [...a2] = a1;

擴展運算符提供了數組合並的新寫法。

  1.  
    // ES5
  2.  
    [ 1, 2].concat(more)
  3.  
    // ES6
  4.  
    [ 1, 2, ...more]
  5.  
     
  6.  
    var arr1 = [ 'a', 'b'];
  7.  
    var arr2 = [ 'c'];
  8.  
    var arr3 = [ 'd', 'e'];
  9.  
     
  10.  
    // ES5的合併數組
  11.  
    arr1.concat(arr2, arr3);
  12.  
    // [ 'a', 'b', 'c', 'd', 'e' ]
  13.  
     
  14.  
    // ES6的合併數組
  15.  
    [...arr1, ...arr2, ...arr3]
  16.  
    // [ 'a', 'b', 'c', 'd', 'e' ]


6、箭頭函數

ES6 容許使用「箭頭」(=>)定義函數。

  1.  
    var f = v => v;
  2.  
    //上面的箭頭函數等同於:
  3.  
    var f = function(v) {
  4.  
    return v;
  5.  
    };

若是箭頭函數的代碼塊部分多於一條語句,就要使用大括號將它們括起來,而且使用return語句返回。若是箭頭函數不須要參數或須要多個參數,就使用一個圓括號表明參數部分。

  1.  
    var f = () => 5;
  2.  
    // 等同於
  3.  
    var f = function () { return 5 };

因爲大括號被解釋爲代碼塊,因此若是箭頭函數直接返回一個對象,必須在對象外面加上括號,不然會報錯。

  1.  
    // 報錯
  2.  
    let getTempItem = id => { id: id, name: "Temp" };
  3.  
     
  4.  
    // 不報錯
  5.  
    let getTempItem = id => ({ id: id, name: "Temp" });

箭頭函數能夠與變量解構結合使用。

  1.  
    const full = ({ first, last }) => first + ' ' + last;
  2.  
    // 等同於
  3.  
    function full(person) {
  4.  
    return person.first + ' ' + person.last;
  5.  
    }

箭頭函數的一個用處是簡化回調函數。

  1.  
    // 正常函數寫法
  2.  
    [ 1, 2, 3].map( function (x) {
  3.  
    return x * x;
  4.  
    });
  5.  
     
  6.  
    // 箭頭函數寫法
  7.  
    [ 1, 2, 3].map( x => x * x);

箭頭函數有幾個使用注意點。
(1)函數體內的this對象,就是定義時所在的對象,而不是使用時所在的對象
(2)不能夠看成構造函數,也就是說,不可使用new命令,不然會拋出一個錯誤。
(3)不可使用arguments對象,該對象在函數體內不存在。若是要用,能夠用 rest 參數代替。
(4)不可使用yield命令,所以箭頭函數不能用做 Generator 函數。
上面四點中,第一點尤爲值得注意。this對象的指向是可變的,可是在箭頭函數中,它是固定的。

  1.  
    function foo() {
  2.  
    setTimeout( () => {
  3.  
    console.log( 'id:', this.id);
  4.  
    }, 100);
  5.  
    }
  6.  
     
  7.  
    var id = 21;
  8.  
     
  9.  
    foo.call({ id: 42 });

上面代碼中,setTimeout的參數是一個箭頭函數,這個箭頭函數的定義生效是在foo函數生成時,而它的真正執行要等到 100 毫秒後。若是是普通函數,執行時this應該指向全局對象window,這時應該輸出21。可是,箭頭函數致使this老是指向函數定義生效時所在的對象(本例是{id: 42}),因此輸出的是42。

7、ES6對象

 

ES6 容許直接寫入變量和函數,做爲對象的屬性和方法。這樣的書寫更加簡潔。

  1.  
    const foo = 'bar';
  2.  
    const baz = {foo};
  3.  
    baz // {foo: "bar"}

ES6 容許在對象之中,直接寫變量。這時,屬性名爲變量名, 屬性值爲變量的值。下面是另外一個例子。

 

  1.  
    function f(x, y) {
  2.  
    return {x, y};
  3.  
    }
  4.  
     
  5.  
    // 等同於
  6.  
     
  7.  
    function f(x, y) {
  8.  
    return { x: x, y: y};
  9.  
    }
  10.  
     
  11.  
    f( 1, 2) // Object {x: 1, y: 2}

函數的name屬性,返回函數名。對象方法也是函數,所以也有name屬性。

 

  1.  
    const person = {
  2.  
    sayName() {
  3.  
    console.log( 'hello!');
  4.  
    },
  5.  
    };
  6.  
     
  7.  
    person.sayName.name // "sayName"

在對象的繼承、原型和構造函數上ES6提供了更接近傳統語言的寫法,引入了Class(類)這個概念。新的class寫法讓對象原型的寫法更加清晰、更像面向對象編程的語法,也更加通俗易懂。

 

  1.  
    class Animal {
  2.  
    constructor(){
  3.  
    this.type = 'animal'
  4.  
    }
  5.  
    says(say){
  6.  
    console.log( this.type + ' says ' + say)
  7.  
    }
  8.  
    }
  9.  
     
  10.  
    let animal = new Animal()
  11.  
    animal.says( 'hello') //animal says hello
  12.  
     
  13.  
    class Cat extends Animal {
  14.  
    constructor(){
  15.  
    super()
  16.  
    this.type = 'cat'
  17.  
    }
  18.  
    }
  19.  
     
  20.  
    let cat = new Cat()
  21.  
    cat.says( 'hello') //cat says hello

上面代碼首先用class定義了一個「類」,能夠看到裏面有一個constructor方法,這就是構造方法,而this關鍵字則表明實例對象。簡單地說,constructor內定義的方法和屬性是實例對象本身的,而constructor外定義的方法和屬性則是全部實例對象能夠共享的
Class之間能夠經過extends關鍵字實現繼承,這比ES5的經過修改原型鏈實現繼承,要清晰和方便不少。上面定義了一個Cat類,該類經過extends關鍵字,繼承了Animal類的全部屬性和方法。
super關鍵字,它指代父類的實例(即父類的this對象)。子類必須在constructor方法中調用super方法,不然新建實例時會報錯。這是由於子類沒有本身的this對象,而是繼承父類的this對象,而後對其進行加工。若是不調用super方法,子類就得不到this對象。
ES6的繼承機制,實質是先創造父類的實例對象this(因此必須先調用super方法),而後再用子類的構造函數修改this

8、遍歷方法

ES6 一共有 5 種方法能夠遍歷對象的屬性。
(1)for...in
for...in循環遍歷對象自身的和繼承的可枚舉屬性(不含 Symbol 屬性)。
(2)Object.keys(obj)
Object.keys返回一個數組,包括對象自身的(不含繼承的)全部可枚舉屬性(不含 Symbol 屬性)的鍵名。
(3)Object.getOwnPropertyNames(obj)
Object.getOwnPropertyNames返回一個數組,包含對象自身的全部屬性(不含 Symbol 屬性,可是包括不可枚舉屬性)的鍵名。
(4)Object.getOwnPropertySymbols(obj)
Object.getOwnPropertySymbols返回一個數組,包含對象自身的全部 Symbol 屬性的鍵名。

(5)Reflect.ownKeys(obj)
Reflect.ownKeys返回一個數組,包含對象自身的全部鍵名,無論鍵名是 Symbol 或字符串,也無論是否可枚舉。
以上的 5 種方法遍歷對象的鍵名,都遵照一樣的屬性遍歷的次序規則。

  1. 首先遍歷全部數值鍵,按照數值升序排列。
  2. 其次遍歷全部字符串鍵,按照加入時間升序排列。
  3. 最後遍歷全部 Symbol 鍵,按照加入時間升序排列。

9、Symbol

 

ES6 引入了一種新的原始數據類型Symbol,表示獨一無二的值。它是 JavaScript 語言的第七種數據類型,前六種是:undefined、null、布爾值(Boolean)、字符串(String)、數值(Number)、對象(Object)。
Symbol 值經過Symbol函數生成。這就是說,對象的屬性名如今能夠有兩種類型,一種是原來就有的字符串,另外一種就是新增的 Symbol 類型。凡是屬性名屬於 Symbol 類型,就都是獨一無二的,能夠保證不會與其餘屬性名產生衝突。

  1.  
    let s1 = Symbol( 'foo');
  2.  
    let s2 = Symbol( 'bar');
  3.  
     
  4.  
    s1 // Symbol(foo)
  5.  
    s2 // Symbol(bar)
  6.  
     
  7.  
    s1.toString() // "Symbol(foo)"
  8.  
    s2.toString() // "Symbol(bar)"

因爲每個 Symbol 值都是不相等的,這意味着 Symbol 值能夠做爲標識符,用於對象的屬性名,就能保證不會出現同名的屬性。這對於一個對象由多個模塊構成的狀況很是有用,能防止某一個鍵被不當心改寫或覆蓋(Symbol 值做爲對象屬性名時,不能用點運算符,由於點運算符後面老是字符串,因此不會讀取mySymbol做爲標識名所指代的那個值,致使a的屬性名其實是一個字符串,而不是一個 Symbol 值)。

  1.  
    let mySymbol = Symbol();
  2.  
     
  3.  
    // 第一種寫法
  4.  
    let a = {};
  5.  
    a[mySymbol] = 'Hello!';
  6.  
     
  7.  
    // 第二種寫法
  8.  
    let a = {
  9.  
    [mySymbol]: 'Hello!'
  10.  
    };
  11.  
     
  12.  
    // 第三種寫法
  13.  
    let a = {};
  14.  
    Object.defineProperty(a, mySymbol, { value: 'Hello!' });
  15.  
     
  16.  
    // 以上寫法都獲得一樣結果
  17.  
    a[mySymbol] // "Hello!"

Object.getOwnPropertySymbols方法返回一個數組,成員是當前對象的全部用做屬性名的 Symbol 值。

  1.  
    const obj = {};
  2.  
    let a = Symbol( 'a');
  3.  
    let b = Symbol( 'b');
  4.  
     
  5.  
    obj[a] = 'Hello';
  6.  
    obj[b] = 'World';
  7.  
     
  8.  
    const objectSymbols = Object.getOwnPropertySymbols(obj);
  9.  
     
  10.  
    objectSymbols
  11.  
    // [Symbol(a), Symbol(b)]

Symbol能夠用於實現單例模式:

 

  1.  
    // mod.js
  2.  
    const FOO_KEY = Symbol.for( 'foo');
  3.  
     
  4.  
    function A() {
  5.  
    this.foo = 'hello';
  6.  
    }
  7.  
     
  8.  
    if (!global[FOO_KEY]) {
  9.  
    global[FOO_KEY] = new A();
  10.  
    }
  11.  
     
  12.  
    module.exports = global[FOO_KEY];

上面代碼中,能夠保證global[FOO_KEY]不會被無心間覆蓋,但仍是能夠被改寫

10、Set和Map

 

ES6 提供了新的數據結構 Set。它相似於數組,可是成員的值都是惟一的,沒有重複的值。Set 自己是一個構造函數,用來生成 Set 數據結構。

 

  1.  
    const s = new Set();
  2.  
     
  3.  
    [ 2, 3, 5, 4, 5, 2, 2].forEach( x => s.add(x));
  4.  
     
  5.  
    for ( let i of s) {
  6.  
    console.log(i);
  7.  
    }
  8.  
    // 2 3 5 4

Set 函數能夠接受一個數組(或者具備 iterable 接口的其餘數據結構)做爲參數,用來初始化。

 

  1.  
    // 例一
  2.  
    const set = new Set([ 1, 2, 3, 4, 4]);
  3.  
    [...set]
  4.  
    // [1, 2, 3, 4]
  5.  
     
  6.  
    // 例二
  7.  
    const items = new Set([ 1, 2, 3, 4, 5, 5, 5, 5]);
  8.  
    items.size // 5

Set的遍歷順序就是插入順序。這個特性有時很是有用,好比使用 Set 保存一個回調函數列表,調用時就能保證按照添加順序調用。
keys方法、values方法、entries方法返回的都是遍歷器對象,因爲 Set 結構沒有鍵名,只有鍵值(或者說鍵名和鍵值是同一個值),因此keys方法和values方法的行爲徹底一致。
ES6 提供了 Map 數據結構。它相似於對象,也是鍵值對的集合,可是「鍵」的範圍不限於字符串,各類類型的值(包括對象)均可以看成鍵。也就是說,Object 結構提供了「字符串—值」的對應,Map 結構提供了「值—值」的對應,是一種更完善的 Hash 結構實現。若是你須要「鍵值對」的數據結構,Map 比 Object 更合適。

  1.  
    const map = new Map([
  2.  
    [ 'name', '張三'],
  3.  
    [ 'title', 'Author']
  4.  
    ]);
  5.  
     
  6.  
    map.size // 2
  7.  
    map.has( 'name') // true
  8.  
    map.get( 'name') // "張三"
  9.  
    map.has( 'title') // true
  10.  
    map.get( 'title') // "Author"

與其餘數據結構的互相轉換

(1)Map 轉爲數組
前面已經提過,Map 轉爲數組最方便的方法,就是使用擴展運算符(...)

  1.  
    const myMap = new Map()
  2.  
    .set( true, 7)
  3.  
    .set({ foo: 3}, [ 'abc']);
  4.  
    [...myMap]
  5.  
    // [ [ true, 7 ], [ { foo: 3 }, [ 'abc' ] ] ]

(2)數組 轉爲 Map
將數組傳入 Map 構造函數,就能夠轉爲 Map。

  1.  
    new Map([
  2.  
    [ true, 7],
  3.  
    [{ foo: 3}, [ 'abc']]
  4.  
    ])
  5.  
    // Map {
  6.  
    // true => 7,
  7.  
    // Object {foo: 3} => ['abc']
  8.  
    // }

(3)Map 轉爲對象
若是全部 Map 的鍵都是字符串,它能夠轉爲對象。

  1.  
    function strMapToObj(strMap) {
  2.  
    let obj = Object.create( null);
  3.  
    for ( let [k,v] of strMap) {
  4.  
    obj[k] = v;
  5.  
    }
  6.  
    return obj;
  7.  
    }
  8.  
     
  9.  
    const myMap = new Map()
  10.  
    .set( 'yes', true)
  11.  
    .set( 'no', false);
  12.  
    strMapToObj(myMap)
  13.  
    // { yes: true, no: false }

(4)對象轉爲 Map

 

  1.  
    function objToStrMap(obj) {
  2.  
    let strMap = new Map();
  3.  
    for ( let k of Object.keys(obj)) {
  4.  
    strMap.set(k, obj[k]);
  5.  
    }
  6.  
    return strMap;
  7.  
    }
  8.  
     
  9.  
    objToStrMap({ yes: true, no: false})
  10.  
    // Map {"yes" => true, "no" => false}

(5)Map 轉爲 JSON
Map 轉爲 JSON 要區分兩種狀況。一種狀況是,Map 的鍵名都是字符串,這時能夠選擇轉爲對象 JSON。

  1.  
    function strMapToJson(strMap) {
  2.  
    return JSON.stringify(strMapToObj(strMap));
  3.  
    }
  4.  
     
  5.  
    let myMap = new Map().set( 'yes', true).set( 'no', false);
  6.  
    strMapToJson(myMap)
  7.  
    // '{"yes":true,"no":false}'

另外一種狀況是,Map 的鍵名有非字符串,這時能夠選擇轉爲數組 JSON。

  1.  
    function mapToArrayJson(map) {
  2.  
    return JSON.stringify([...map]);
  3.  
    }
  4.  
     
  5.  
    let myMap = new Map().set( true, 7).set({ foo: 3}, [ 'abc']);
  6.  
    mapToArrayJson(myMap)
  7.  
    // '[[true,7],[{"foo":3},["abc"]]]'

(6)JSON 轉爲 Map
JSON 轉爲 Map,正常狀況下,全部鍵名都是字符串。

  1.  
    function jsonToStrMap(jsonStr) {
  2.  
    return objToStrMap( JSON.parse(jsonStr));
  3.  
    }
  4.  
     
  5.  
    jsonToStrMap( '{"yes": true, "no": false}')
  6.  
    // Map {'yes' => true, 'no' => false}

 

11、模塊(module)體系

 

ES6 模塊不是對象,而是經過export命令顯式指定輸出的代碼,再經過import命令輸入。

  1.  
    // ES6模塊
  2.  
    import { stat, exists, readFile } from 'fs';

上面代碼的實質是從fs模塊加載 3 個方法,其餘方法不加載。這種加載稱爲「編譯時加載」或者靜態加載,即 ES6 能夠在編譯時就完成模塊加載,效率要比 CommonJS 模塊的加載方式高。固然,這也致使了無法引用 ES6 模塊自己,由於它不是對象
因爲 ES6 模塊是編譯時加載,使得靜態分析成爲可能。有了它,就能進一步拓寬 JavaScript 的語法,好比引入宏(macro)和類型檢驗(type system)這些只能靠靜態分析實現的功能。
除了靜態加載帶來的各類好處,ES6 模塊還有如下好處。

  1. 再也不須要UMD模塊格式了,未來服務器和瀏覽器都會支持 ES6 模塊格式。目前,經過各類工具庫,其實已經作到了這一點。
  2. 未來瀏覽器的新 API 就能用模塊格式提供,再也不必須作成全局變量或者navigator對象的屬性。
  3. 再也不須要對象做爲命名空間(好比Math對象),將來這些功能能夠經過模塊提供。

ES6 的模塊自動採用嚴格模式,無論你有沒有在模塊頭部加上"use strict";。嚴格模式主要有如下限制。

  • 變量必須聲明後再使用
  • 函數的參數不能有同名屬性,不然報錯
  • 不能使用with語句
  • 不能對只讀屬性賦值,不然報錯
  • 不能使用前綴 0 表示八進制數,不然報錯
  • 不能刪除不可刪除的屬性,不然報錯
  • 不能刪除變量delete prop,會報錯,只能刪除屬性delete global[prop]
  • eval不會在它的外層做用域引入變量
  • eval和arguments不能被從新賦值
  • arguments不會自動反映函數參數的變化
  • 不能使用arguments.callee
  • 不能使用arguments.caller
  • 禁止this指向全局對象
  • 不能使用fn.caller和fn.arguments獲取函數調用的堆棧
  • 增長了保留字(好比protected、static和interface)

export 命令

 

模塊功能主要由兩個命令構成:export和import。export命令用於規定模塊的對外接口,import命令用於輸入其餘模塊提供的功能。一個模塊就是一個獨立的文件。該文件內部的全部變量,外部沒法獲取。若是你但願外部可以讀取模塊內部的某個變量,就必須使用export關鍵字輸出該變量。下面是一個 JS 文件,裏面使用export命令輸出變量。

  1.  
    // profile.js
  2.  
    export var firstName = 'Michael';
  3.  
    export var lastName = 'Jackson';
  4.  
    export var year = 1958;

上面代碼是profile.js文件,保存了用戶信息。ES6 將其視爲一個模塊,裏面用export命令對外部輸出了三個變量。export的寫法,除了像上面這樣,還有另一種。

 

  1.  
    // profile.js
  2.  
    var firstName = 'Michael';
  3.  
    var lastName = 'Jackson';
  4.  
    var year = 1958;
  5.  
     
  6.  
    export {firstName, lastName, year};

export命令除了輸出變量,還能夠輸出函數或類(class)。一般狀況下,export輸出的變量就是原本的名字,可是可使用as關鍵字重命名。

  1.  
    function v1() { ... }
  2.  
    function v2() { ... }
  3.  
     
  4.  
    export {
  5.  
    v1 as streamV1,
  6.  
    v2 as streamV2,
  7.  
    v2 as streamLatestVersion
  8.  
    };

export語句輸出的接口,與其對應的值是動態綁定關係,即經過該接口,能夠取到模塊內部實時的值。
export命令能夠出如今模塊的任何位置,只要處於模塊頂層就能夠。若是處於塊級做用域內,就會報錯,下一節的import命令也是如此。這是由於處於條件代碼塊之中,就無法作靜態優化了,違背了 ES6 模塊的設計初衷。

  1.  
    // 報錯
  2.  
    var m = 1;
  3.  
    export m;
  4.  
     
  5.  
    // 寫法一
  6.  
    export var m = 1;
  7.  
     
  8.  
    // 寫法二
  9.  
    var m = 1;
  10.  
    export {m};

import 命令

使用export命令定義了模塊的對外接口之後,其餘 JS 文件就能夠經過import命令加載這個模塊。

  1.  
    // main.js
  2.  
    import {firstName, lastName, year} from './profile.js';
  3.  
     
  4.  
    function setName(element) {
  5.  
    element.textContent = firstName + ' ' + lastName;
  6.  
    }

上面代碼的import命令,用於加載profile.js文件,並從中輸入變量。import命令接受一對大括號,裏面指定要從其餘模塊導入的變量名。大括號裏面的變量名,必須與被導入模塊(profile.js)對外接口的名稱相同。若是想爲輸入的變量從新取一個名字,import命令要使用as關鍵字,將輸入的變量重命名。

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

import命令具備提高效果,會提高到整個模塊的頭部,首先執行。

export default 命令

 

使用import命令的時候,用戶須要知道所要加載的變量名或函數名,不然沒法加載。可是,用戶確定但願快速上手,未必願意閱讀文檔,去了解模塊有哪些屬性和方法。爲了給用戶提供方便,讓他們不用閱讀文檔就能加載模塊,就要用到export default命令,爲模塊指定默認輸出。

 

  1.  
    // export-default.js
  2.  
    export default function () {
  3.  
    console.log( 'foo');
  4.  
    }

上面代碼是一個模塊文件export-default.js,它的默認輸出是一個函數。其餘模塊加載該模塊時,import命令能夠爲該匿名函數指定任意名字。

 

  1.  
    // import-default.js
  2.  
    import customName from './export-default';
  3.  
    customName(); // 'foo'

上面代碼的import命令,能夠用任意名稱指向export-default.js輸出的方法,這時就不須要知道原模塊輸出的函數名。須要注意的是,這時import命令後面,不使用大括號。
export default命令用於指定模塊的默認輸出。顯然,一個模塊只能有一個默認輸出,所以export default命令只能使用一次。因此,import命令後面纔不用加大括號,由於只可能惟一對應export default命令。本質上,export default就是輸出一個叫作default的變量或方法,而後系統容許你爲它取任意名字。

export 與 import 的複合寫法

 

若是在一個模塊之中,先輸入後輸出同一個模塊,import語句能夠與export語句寫在一塊兒。

  1.  
    export { foo, bar } from 'my_module';
  2.  
     
  3.  
    // 等同於
  4.  
    import { foo, bar } from 'my_module';
  5.  
    export { foo, bar };

 

import和require的區別

引擎處理import語句是在編譯時,這時不會去分析或執行if語句,因此import語句放在if代碼塊之中毫無心義,所以會報句法錯誤,而不是執行時錯誤。也就是說,import和export命令只能在模塊的頂層,不能在代碼塊之中(好比,在if代碼塊之中,或在函數之中)。
這樣的設計,當然有利於編譯器提升效率,但也致使沒法在運行時加載模塊。在語法上,條件加載就不可能實現。若是import命令要取代 Node 的require方法,這就造成了一個障礙。由於require是運行時加載模塊,import命令沒法取代require的動態加載功能。

  1.  
    const path = './' + fileName;
  2.  
    const myModual = require(path);

上面的語句就是動態加載,require到底加載哪個模塊,只有運行時才知道。import語句作不到這一點。

import()加載模塊成功之後,這個模塊會做爲一個對象,看成then方法的參數。所以,可使用對象解構賦值的語法,獲取輸出接口。

  1.  
    import( './myModule.js')
  2.  
    .then( ({export1, export2}) => {
  3.  
    // ...·
  4.  
    });

上面代碼中,export1和export2都是myModule.js的輸出接口,能夠解構得到。若是模塊有default輸出接口,能夠用參數直接得到。

如上就是ES6中比較容易識別出的關鍵點,實際上在兩天半的ES6使用中也確實見到了如上的用法,感謝阮一峯老師的博客,先總結到這裏,留待後補?

相關文章
相關標籤/搜索