ECMAScript 6不徹底教程

1. 嘗試ES6

這裏有三種簡單地方式用於ES6編程:html

  1. Web瀏覽器:使用Babel REPL,能夠將ES6編譯成ES5的平臺,而且並不須要安裝。node

  2. 命令行:使用babel-node,能夠執行ES6的Node.js版本(會在內部編譯es5)。須要經過npm安裝。git

  3. 各類js引擎:根據ES6語法兼容表,找出被支持的ES6功能。es6

對於第一點和第二點,這有更多細節。github

1.1 Babel REPL

Babel REPL主要有四個部分:npm

一、左上角包含es6源代碼
二、左下角能夠查看es6中的語法錯誤
三、右上角是es6被編譯成es5的源代碼
四、右下角是經過console.log()的輸出結果編程

1.2 babel-node

babel-node能夠經過npm安裝:數組

$ npm install --global babel

跟node同樣,能夠命令開啓REPL交互:瀏覽器

$ babel-node

一旦開啓REPL,就能夠去執行ES6代碼:babel

> let arr = [1, 2, 3];
> arr.map(x => x * x)
[ 1, 4, 9 ]

注意:babel-node目前不支持多行輸入
Babel官網上有管多關於Babel CLI的工具。

2. 從var到let/const

es6有兩種新的方式來聲明變量:
一、let用於聲明塊級做用於變量
二、const用於聲明常量,其值不能被改變。

letconst能夠用來替代var聲明變量,可是不能盲目使用,由於不一樣的做用域會改變代碼的行爲。例如:

var x = 3;
    function func(randomize) {
        if (randomize) {
            var x = Math.random(); // (A) scope: whole function
            return x;
        }
        return x; // accesses the x from line A
    }
    func(false); // undefined

func()會意外地返回undefined。你能夠重寫這部分代碼,就知道爲何返回undefined了:

var x = 3;
    function func(randomize) {
        var x;
        if (randomize) {
            x = Math.random();
            return x;
        }
        return x;
    }
    func(false); // undefined

若是用let代替以前的var,就會獲得不一樣的結果:

let x = 3;
    function func(randomize) {
        if (randomize) {
            let x = Math.random();
            return x;
        }
        return x;
    }
    func(false); // 3

所以,盲目地用letconst代替var是有風險的,個人建議是:
一、只在新代碼中使用letconst
二、丟棄舊代碼或仔細認證

更多信息:es6中的變量和做用域

3. 從IIFEs到代碼塊

在es5中,若是要維護本地變量,不得不使用IIFE:

(function () {  // open IIFE
        var tmp = ···;
        ···
    }());  // close IIFE
    
    console.log(tmp); // ReferenceError

而在es6中,則只須要代碼塊和let聲明:

{  // open block
        let tmp = ···;
        ···
    }  // close block
    
    console.log(tmp); // ReferenceError

更多信息:避免IIFEs

4. 從字符串拼接到模板常量

在es6中,對於字符串插值和多行字符串,Javscript能獲得其字面值。

4.1 字符串插值

在es5中,是將結果放在字符串中進行拼接:

function printCoord(x, y) {
        console.log('('+x+', '+y+')');
    }

es6中,經過字符串字面模板,能夠在字符串中插入變量值:

function printCoord(x, y) {
        console.log(`(${x}, ${y})`);
    }
4.2 多行字符串

模板字面量也能輸出多行字符串。
例如,在es5中,輸出多行字符串,得這樣:

var HTML5_SKELETON =
        '<!doctype html>\n' +
        '<html>\n' +
        '<head>\n' +
        '    <meta charset="UTF-8">\n' +
        '    <title></title>\n' +
        '</head>\n' +
        '<body>\n' +
        '</body>\n' +
        '</html>\n';

若是經過反斜線轉義新行,代碼看起來會舒服點(但仍是要顯示的添加新行):

var HTML5_SKELETON = '\
        <!doctype html>\n\
        <html>\n\
        <head>\n\
            <meta charset="UTF-8">\n\
            <title></title>\n\
        </head>\n\
        <body>\n\
        </body>\n\
        </html>';

而es6得模板字面量能跨多行:

const HTML5_SKELETON = `
        <!doctype html>
        <html>
        <head>
            <meta charset="UTF-8">
            <title></title>
        </head>
        <body>
        </body>
        </html>`;

更多信息:字面量模板

5. 從函數表達式到箭頭函數

在es5中,在函數表達式中必須當心使用this。在示例中,我建立了輔助變量_this_,以便於我能再line B處使用:

function UiComponent {
        var _this = this; // (A)
        var button = document.getElementById('myButton');
        button.addEventListener('click', function () {
            console.log('CLICK');
            _this.handleClick(); // (B)
        });
    }
    UiComponent.prototype.handleClick = function () {
        ···
    };

在es6中,可使用箭頭函數,它不會影響this

class UiComponent {
        constructor() {
            let button = document.getElementById('myButton');
            button.addEventListener('click', () => {
                console.log('CLICK');
                this.handleClick(); // (A)
            });
        }
        handleClick() {
            ···
        }
    }

對於只返回結果的短小回調函數,箭頭函數是很是便利的。
在es5中,以下的回調是相對冗長的:

var arr = [1, 2, 3];
    var squares = arr.map(function (x) { return x * x });

在es6中,箭頭函數會更簡潔:

let arr = [1, 2, 3];
    let squares = arr.map(x => x * x);

在定義參數時,若是隻有一個參數,括號是能夠省略的。於是,(x) => x x and x => x x 都是容許的。
更多信息:箭頭函數

6. 處理多個返回值

一些函數或方法能經過數組或對象返回多個值。在es5中,老是須要建立中間變量來訪問返回值。es6中,可使用解構。

6.1 經過數組返回多個值

exec()返回類數組對象的捕獲組。es5中,當要訪問捕獲組的值時,須要一箇中間變量:

var matchObj =
        /^(\d\d\d\d)-(\d\d)-(\d\d)$/
        .exec('2999-12-31');
    var year = matchObj[1];
    var month = matchObj[2];
    var day = matchObj[3];

es6中,解構使代碼更簡單:

let [, year, month, day] =
        /^(\d\d\d\d)-(\d\d)-(\d\d)$/
        .exec('2999-12-31');

空得逗號表示跳過數組的第一個元素。

6.2 經過對象返回多個值

Object.getOwnPropertyDescriptor()會返回一個屬性描述符對象。

es5中,仍須要一箇中間變量來訪問你感興趣的對象屬性值:

var obj = { foo: 123 };
    
    var propDesc = Object.getOwnPropertyDescriptor(obj, 'foo');
    var writable = propDesc.writable;
    var configurable = propDesc.configurable;
    
    console.log(writable, configurable); // true true

es6,可使用解構:

let obj = { foo: 123 };
    
    let {writable, configurable} =
        Object.getOwnPropertyDescriptor(obj, 'foo');
    
    console.log(writable, configurable); // true true

{writable, configurable}的值以下:

{ writable: writable, configurable: configurable }

更多信息:解構

7. 從for到forEach再到for-of

先說es6,通常會這樣迭代數組:

var arr = ['a', 'b', 'c'];
    for (var i=0; i<arr.length; i++) {
        var elem = arr[i];
        console.log(elem);
    }

也可使用ArrayforEach

arr.forEach(function (elem) {
        console.log(elem);
    });

for循環的優勢是能夠中斷,forEach的優勢是簡潔。
而es6的for-of循環則結合了二者的優勢:

let arr = ['a', 'b', 'c'];
    for (let elem of arr) {
        console.log(elem);
    }

for-of循環也能經過數組的entries方法和解構返回數組的索引和對應的值:

for (let [index, elem] of arr.entries()) {
        console.log(index+'. '+elem);
    }

更多信息:for-of循環

8. 參數默認值

es5中,指定參數默認值得這樣:

function foo(x, y) {
        x = x || 0;
        y = y || 0;
        ···
    }

es6有個更簡潔的語法:

function foo(x=0, y=0) {
        ···
    }

es6語法還一個優勢:參數默認值只能被undefined觸發,而在es5中,則會被任何z爲false的值觸發。

更多信息:參數默認值

9. 命名參數

在Javascript中,命名參數的廣泛方式是對象字面量:

selectEntries({ start: 0, end: -1 });

其等價實現:

function selectEntries(options) {
        var start = options.start || 0;
        var end = options.end || -1;
        var step = options.step || 1;
        ···
    }

es6中,解構是語法更簡單:

function selectEntries({ start=0, end=-1, step=1 }) {
        ···
    }
9.1 使參數可選

es5中使參數可選的作法是這樣的:

function selectEntries(options) {
        options = options || {}; // (A)
        var start = options.start || 0;
        var end = options.end || -1;
        var step = options.step || 1;
        ···
    }

es6中,能夠指定{}爲參數的默認值:

function selectEntries({ start=0, end=-1, step=1 } = {}) {
        ···
    }

更多信息:模擬命名參數

10. 從arguments到參數擴展

es5中,若想讓方法或函數接受任意個數的參數,就必須使用指定的arguments變量:

function logAllArguments() {
        for (var i=0; i < arguments.length; i++) {
            console.log(arguments[i]);
        }
    }

es6中,可使用...操做達到一樣地效果:

function logAllArguments(...args) {
        for (let arg of args) {
            console.log(arg);
        }
    }

還有更nice的語法:

function format(pattern, ...args) {
        ···
    }

而es5中的處理則相對笨拙:

function format() {
        var pattern = arguments[0];
        var args = arguments.slice(1);
        ···
    }

更多信息:Rest parameters

11. 從apply到散佈操做符(...)

es5中,apply()會將數組轉會成參數,es6中使用散佈操做符達到一樣地目的。

11.1 Math.max()

es5-->apply():

> Math.max.apply(null, [-1, 5, 11, 3])
    11

es6-->spread operator:

> Math.max(...[-1, 5, 11, 3])
    11
11.2 Array.prototype.push()

es5-->apply():

var arr1 = ['a', 'b'];
    var arr2 = ['c', 'd'];
    
    arr1.push.apply(arr1, arr2);
        // arr1 is now ['a', 'b', 'c', 'd']

es6-->spread operator:

let arr1 = ['a', 'b'];
    let arr2 = ['c', 'd'];
    
    arr1.push(...arr2);
        // arr1 is now ['a', 'b', 'c', 'd']

更多信息:spread operator

12. 從concat()到(...)

ES5 – concat():

var arr1 = ['a', 'b'];
    var arr2 = ['c'];
    var arr3 = ['d', 'e'];
    
    console.log(arr1.concat(arr2, arr3));
        // [ 'a', 'b', 'c', 'd', 'e' ]

ES6 – spread operator:

let arr1 = ['a', 'b'];
    let arr2 = ['c'];
    let arr3 = ['d', 'e'];
    
    console.log([...arr1, ...arr2, ...arr3]);
        // [ 'a', 'b', 'c', 'd', 'e' ]

更多信息:spread operator

13. 從構造器到類

對於構造器語法,es6的類則更簡便。

13.1 基本類

es5中實現一個基本類以下:

function Person(name) {
        this.name = name;
    }
    Person.prototype.describe = function () {
        return 'Person called '+this.name;
    };

es6中,類提供了更簡潔的語法:

class Person {
        constructor(name) {
            this.name = name;
        }
        describe() {
            return 'Person called '+this.name;
        }
    }

13.2 派生類

es5實現了類的派生,下面是實現派生類的一種規範方法:

function Employee(name, title) {
        Person.call(this, name); // super(name)
        this.title = title;
    }
    Employee.prototype = Object.create(Person.prototype);
    Employee.prototype.constructor = Employee;
    Employee.prototype.describe = function () {
        return Person.prototype.describe.call(this) // super.describe()
               + ' (' + this.title + ')';
    };

es6內置了類派生語法,要藉助extends關鍵字:

class Employee extends Person {
        constructor(name, title) {
            super(name);
            this.title = title;
        }
        describe() {
            return super.describe() + ' (' + this.title + ')';
        }
    }

更多信息:

14. 從自定義error到Error派生

跟上面有點相似。es5中自定義error:

function MyError() {
        // Use Error as a function
        var superInstance = Error.apply(null, arguments);
        copyOwnPropertiesFrom(this, superInstance);
    }
    MyError.prototype = Object.create(Error.prototype);
    MyError.prototype.constructor = MyError;

es6經過派生實現:

class MyError extends Error {
    }

更多信心:Subclassing built-in constructors

15. 從對象字面量的函數表達式和方法定義

語法上的差異。es5實現:

var obj = {
        foo: function () {
            ···
        },
        bar: function () {
            this.foo();
        }, // trailing comma is legal in ES5
    }

es6:

let obj = {
        foo() {
            ···
        },
        bar() {
            this.foo();
        },
    }

更多信息:方法定義

16. 從對象到圖

es5中利用對象來實現圖的數據結構,須要將對象的prototype指向null,並保證__proto__上沒有對應的鍵。

var dict = Object.create(null);
    function countWords(word) {
        var escapedWord = escapeKey(word);
        if (escapedWord in dict) {
            dict[escapedWord]++;
        } else {
            dict[escapedWord] = 1;
        }
    }
    function escapeKey(key) {
        if (key.indexOf('__proto__') === 0) {
            return key+'%';
        } else {
            return key;
        }
    }

es6則內置了Map數據結構;

let map = new Map();
    function countWords(word) {
        let count = map.get(word) || 0;
        map.set(word, count + 1);
    }

更多信息:Maps and Sets

17. 從CommonJS模塊到es6 模塊

es5中,模塊系統是基於AMD或CommocJS語法。es6內置了模塊語法,但並無獲得Javascript引擎良好支持。

17.1 多個導出

在CommonJS中,能夠這樣實現:

//------ lib.js ------
    var sqrt = Math.sqrt;
    function square(x) {
        return x * x;
    }
    function diag(x, y) {
        return sqrt(square(x) + square(y));
    }
    module.exports = {
        sqrt: sqrt,
        square: square,
        diag: diag,
    };
    
    //------ main1.js ------
    var square = require('lib').square;
    var diag = require('lib').diag;
    
    console.log(square(11)); // 121
    console.log(diag(4, 3)); // 5

es6的語法是醬紫的:

//------ lib.js ------
    export const sqrt = Math.sqrt;
    export function square(x) {
        return x * x;
    }
    export function diag(x, y) {
        return sqrt(square(x) + square(y));
    }
    
    //------ main1.js ------
    import { square, diag } from 'lib';
    console.log(square(11)); // 121
    console.log(diag(4, 3)); // 5

或者做爲一個對象導入:

//------ main2.js ------
    import * as lib from 'lib'; // (A)
    console.log(lib.square(11)); // 121
    console.log(lib.diag(4, 3)); // 5
17.2 單個導出

Node.js繼承了CommonJS的語法,能從模塊導出單個值:

//------ myFunc.js ------
    module.exports = function () { ··· };
    
    //------ main1.js ------
    var myFunc = require('myFunc');
    myFunc();

es6經過export default實現:

//------ myFunc.js ------
    export default function () { ··· } // no semicolon!
    
    //------ main1.js ------
    import myFunc from 'myFunc';
    myFunc();

更多信息:Modules


相關文章:ECMAScript 6新特性介紹
譯文出處:Getting started with ECMAScript 6

相關文章
相關標籤/搜索