[譯] ES6 學習筆記:關於 ES2015 特性的詳細概述

原文地址:http://babeljs.io/docs/learn-...
本文基於Luke Hoban精妙的文章《es6features》,請把star獻給他,你能夠在此嘗試這些特性REPL
javascript

概述

ECMAScript 2015 是 ECMAScript 在2015年6月正式發佈的一套標準。ES2015是對語言的一次富有意義的更新,也是自2009年ES5標準發佈以來,對於該門語言第一次主要的更新。主流JS引擎正在逐步完善對該標準的支持html

查看ECMAScript 2015的詳盡文檔java

ECMAScript 2015的新特性

箭頭函數與詞法 this (Arrows and Lexical This)

箭頭函數經過 => 語法簡化函數的書寫。其與C#,Java 8和CoffeeScript中相關特性有着類似的語法。同時支持表達式和語句。與函數不一樣,箭頭函數與上下文共享詞法做用域中的thisjquery

(注:爲函數指ES5中函數)git

// 表達式
var odds = evens.map(v => v + 1);
var nums = evens.map((v, i) => v + i);

// 語句
nums.forEach(v => {
  if (v % 5 === 0)
    fives.push(v);
});

// 詞法this
var bob = {
  _name: "Bob",
  _friends: [],
  printFriends() {
    this._friends.forEach(f =>
      console.log(this._name + " knows " + f));
  }
};

類 Classes

ES2015中的類是一個基於面向對象原型鏈模式簡單的語法糖。經過簡單方便的聲明模式使得類模式更易用,並鼓勵交互操做。同時,類支持基於原型鏈的繼承,super調用,實例與靜態方法,構造函數。es6

(注:ES5中經過原型鏈模式來實現其餘語言中經過類實現的邏輯,這種模式能夠看做一種類模式)github

class SkinnedMesh extends THREE.Mesh {
  constructor(geometry, materials) {
    super(geometry, materials);

    this.idMatrix = SkinnedMesh.defaultMatrix();
    this.bones = [];
    this.boneMatrices = [];
    //...
  }
  update(camera) {
    //...
    super.update();
  }
  static defaultMatrix() {
    return new THREE.Matrix4();
  }
}

加強的Object字面量 Enhanced Object Literals

在ES6中對象被增強以支持諸如:構造時的原型設定,簡化foo: foo聲明的寫法,定義方法,進行super調用。總之,這使得對象與類的聯繫更加緊密,也讓基於對象的設計從中獲得了一樣的便利。web

var obj = {
    // 設置原型,"__proto__" or '__proto__' 寫法一樣適用
    __proto__: theProtoObj,
    // Computed property name does not set prototype or trigger early error for
    // 複製 __proto__ 屬性
    ['__proto__']: somethingElse,
    // ‘handler: handler’格式的簡寫
    handler,
    // 方法聲明
    toString() {
     // Super 調用
     return "d " + super.toString();
    },
    // 計算(動態的)屬性名稱
    [ "prop_" + (() => 42)() ]: 42
};

__proto__ 屬性須要原生的支持(注:這裏應該指的是運行環境的支持),而且在前代版本中是 不推薦 使用的。雖然大多數JS引擎現已支持該屬性,但還有一些並不支持。所以,只有在web瀏覽器須要屬性支持時才執行該屬性,就像附件B中所述。在Node環境中也可使用。算法

模版字符串 Template Strings

模版字符串是在構造字符串時使用的語法糖,其與Perl,Python等語言中字符串插值特性相似。有選擇性的添加標籤來定製字符串結構,這樣能夠防止注入攻擊也能基於字符串去構建更高層次的數據結構。編程

// 基本字面量的建立
`This is a pretty little template string.`

// 多行字符串
`In ES5 this is
 not legal.`

// 綁定插值變量
var name = "Bob", time = "today";
`Hello ${name}, how are you ${time}?`

// 非轉移的模版字符串
String.raw`In ES5 "\n" is a line-feed.`

// 構造一個HTTP格式的請求前綴用來解釋替換和構造
GET`http://foo.org/bar?a=${a}&b=${b}
    Content-Type: application/json
    X-Credentials: ${credentials}
    { "foo": ${foo},
      "bar": ${bar}}`(myOnReadyStateChangeHandler);

解構 Restructuring

解構容許在綁定時使用模式匹配,其支持匹配數組與對象。與標準的foo["bar"]查詢相似,解構是故障弱化的,即當沒有匹配值時返回undefined

// 匹配數組
var [a, ,b] = [1,2,3];
a === 1;
b === 3;

// 匹配對象
var { op: a, lhs: { op: b }, rhs: c }
       = getASTNode()

// 匹配對象簡寫
// 在做用域中綁定 `op`, `lhs` 與 `rhs`
var {op, lhs, rhs} = getASTNode()

// 做爲參數使用的場景
function g({name: x}) {
  console.log(x);
}
g({name: 5})

// 故障弱化的解構
var [a] = [];
a === undefined;

// 有默認值狀況下的故障弱化的解構
var [a = 1] = [];
a === 1;

// 解構與默認參數聯用
function r({x, y, w = 10, h = 10}) {
  return x + y + w + h;
}
r({x:1, y:2}) === 23

默認參數 DefaultRest參數 Rest擴展運算符 Spread

設置被調用函數(callee)的默認參數值。在函數調用時,可將數組各項做爲連續參數傳入。在函數末尾的參數,能夠綁定一個用數組傳入的不定長度的參數。Rest參數取代arguments更直接地應用於常見例子中。

function f(x, y=12) {
  // 當沒有輸入或輸入爲undefined時y的值是12
  return x + y;
}
f(3) == 15
function f(x, ...y) {
  // y是一個數組
  return x * y.length;
}
f(3, "hello", true) == 6
function f(x, y, z) {
  return x + y + z;
}
// 將數組中的每一項做爲參數傳入
f(...[1,2,3]) == 6

LetConst

let或const聲明後在解構體中綁定 塊級做用域let是新版的varconst是單次賦值,靜態化限制了在const賦值後再對變量賦值。

function f() {
  {
    let x;
    {
      // 塊級做用域
      const x = "sneaky";
      // 報錯,不能改變常量
      x = "foo";
    }
    // x已由let建立
    x = "bar";
    // 報錯, 已經用let建立過變量x
    let x = "inner";
  }
}

迭代器與for..of循環 (Iterators + For..Of)

迭代器對象容許像CLR IEnumerable或Java Iterable同樣定義迭代器。將for..in 轉換成常見的使用for..of實現的基於迭代器的迭代。不須要實現數組,支持如LINQ式的懶惰設計模式。

let fibonacci = {
  [Symbol.iterator]() {
    let pre = 0, cur = 1;
    return {
      next() {
        [pre, cur] = [cur, pre + cur];
        return { done: false, value: cur }
      }
    }
  }
}

for (var n of fibonacci) {
  // 1000處中止
  if (n > 1000)
    break;
  console.log(n);
}

迭代器基於鴨子模型接口(這裏使用TypeScript語法僅僅爲了說明問題):

interface IteratorResult {
  done: boolean;
  value: any;
}
interface Iterator {
  next(): IteratorResult;

}
interface Iterable {
  [Symbol.iterator](): Iterator
}

使用 polypill 支持:爲了使用迭代器屬性須要引入Babel polyfill

生成器 Generators

經過使用function 與 yield,生成器簡化了迭代器的編寫。當函數聲明時使用function格式時返回一個生成器實例。生成器是迭代器的子類,包含了附加的next與throw。這使得值能夠迴流進生成器,因此yield是一個能夠返回或拋出值的表達式。

值得注意的是在ES7的草案中,使用'await'一樣可以達到這種異步編程的效果。

var fibonacci = {
  [Symbol.iterator]: function*() {
    var pre = 0, cur = 1;
    for (;;) {
      var temp = pre;
      pre = cur;
      cur += temp;
      yield cur;
    }
  }
}

for (var n of fibonacci) {
  // truncate the sequence at 1000
  if (n > 1000)
    break;
  console.log(n);
}

生成器接口定義(這裏使用TypeScript語法僅僅爲了說明問題):

interface Generator extends Iterator {
    next(value?: any): IteratorResult;
    throw(exception: any);
}

使用 polypill 支持:爲了使用迭代器屬性須要引入Babel polyfill

Comprehensions

在Babel6.0中已刪除

Unicode編碼

非破壞性地添加以支持更全面的unicode,這包括:字符串中新的unicode字面量格式以及處理代碼斷點的新正則符號u,同時,新的API能夠在21位的級別上處理字符串。依賴這些新增的支持使得JavaScript構建全球化應用成爲可能。

模塊 Modules

從語言層面對組件定義模塊進行支持。將主流的JavaScript模塊加載方式(AMD, CommonJS)變成標準。由運行環境中的默認加載器決定模塊運行時的行爲。直到獲取並執行完請求的模塊,才執行隱式異步模型中的代碼。

// lib/math.js
export function sum(x, y) {
  return x + y;
}
export var pi = 3.141593;
// app.js
import * as math from "lib/math";
console.log("2π = " + math.sum(math.pi, math.pi));
// otherApp.js
import {sum, pi} from "lib/math";
console.log("2π = " + sum(pi, pi));

一些額外的特性包括export defaultexport *

// lib/mathplusplus.js
export * from "lib/math";
export var e = 2.71828182846;
export default function(x) {
    return Math.exp(x);
}
// app.js
import exp, {pi, e} from "lib/mathplusplus";
console.log("e^π = " + exp(pi));

模塊模式:Babel能夠將ES2015 編譯成多種不一樣的模式,諸如Common.js,AMD,System和UMD。你甚至能夠創造你本身的模式。更多細節請參考

模塊的加載器 Module Loaders

不存在於ES2015中:因爲在以前的ES2015草案中被廢棄,其並未在ECMAScript2015年的規範中實現定義。不過目前這是一項正在進行的工做,而且最終的標準將會寫入WHATWG's Loader規則中。

模塊的加載器支持:

  • 動態加載 Dynamic loading

  • 狀態隔離 State isolation

  • 全局命名空間隔離 Global namespace isolation

  • 可編譯的鉤子函數 Compilation hooks

  • 嵌套虛擬化技術 Nested virtualization

默認的模塊是可配置的,構建的新模塊能夠在孤立/受限的上下文代碼進行代碼的求值和引用。

// 動態加載 – ‘System’ 是默認加載器
System.import("lib/math").then(function(m) {
  alert("2π = " + m.sum(m.pi, m.pi));
});

// 建立一個執行沙盒即新的加載器
var loader = new Loader({
  global: fixup(window) // 替代 ‘console.log’
});
loader.eval("console.log(\"hello world!\");");

// 直接操控模塊的緩存
System.get("jquery");
System.set("jquery", Module({$: $})); // 警告:還未完成

須要添加polypill:因爲Babel默認使用common.js規範,因此並無包含polypill的模塊加載API。詳情查閱

使用模塊加載器:爲了使用模塊加載器,須要告訴Babel使用模塊模式系統。同時,確認檢查System.js

Map + Set + WeakMap + WeakSet

用於提升常見算法中數據結構的高效性。

WeakMap接受對象做爲鍵名(WeakMap的設計目的在於,鍵名是對象的弱引用(垃圾回收機制不將該引用考慮在內),因此其所對應的對象可能會被自動回收。)

(注:紅字部分原文翻譯過來有點變扭,這裏借用ES6入門的描述)

// Sets
var s = new Set();
s.add("hello").add("goodbye").add("hello");
s.size === 2;
s.has("hello") === true;

// Maps
var m = new Map();
m.set("hello", 42);
m.set(s, 34);
m.get(s) == 34;

// Weak Maps
var wm = new WeakMap();
wm.set(s, { extra: 42 });
wm.size === undefined

// Weak Sets
var ws = new WeakSet();
ws.add({ data: 42 });
// 因爲添加的對象沒有別的引用,所以不會被set保留

使用 polypill 支持:爲了在全部環境中正常使用Map,Set,WeakMap和WeakSet須要引入Babel polyfill

代理 Proxies

代理能夠建立包括宿主對象全部可用行爲的對象。其被應用於攔截,對象虛擬化,日誌/描述等多個方面。

// 代理一個普通對象
var target = {};
var handler = {
  get: function (receiver, name) {
    return `Hello, ${name}!`;
  }
};

var p = new Proxy(target, handler);
p.world === "Hello, world!";
// 代理一個函數對象
var target = function () { return "I am the target"; };
var handler = {
  apply: function (receiver, ...args) {
    return "I am the proxy";
  }
};

var p = new Proxy(target, handler);
p() === "I am the proxy";

對於全部運行級別的原操做,都有許多坑要注意:

var handler =
{
  // target.prop
  get: ...,
  // target.prop = value
  set: ...,
  // 'prop' in target
  has: ...,
  // delete target.prop
  deleteProperty: ...,
  // target(...args)
  apply: ...,
  // new target(...args)
  construct: ...,
  // Object.getOwnPropertyDescriptor(target, 'prop')
  getOwnPropertyDescriptor: ...,
  // Object.defineProperty(target, 'prop', descriptor)
  defineProperty: ...,
  // Object.getPrototypeOf(target), Reflect.getPrototypeOf(target),
  // target.__proto__, object.isPrototypeOf(target), object instanceof target
  getPrototypeOf: ...,
  // Object.setPrototypeOf(target), Reflect.setPrototypeOf(target)
  setPrototypeOf: ...,
  // for (let i in target) {}
  enumerate: ...,
  // Object.keys(target)
  ownKeys: ...,
  // Object.preventExtensions(target)
  preventExtensions: ...,
  // Object.isExtensible(target)
  isExtensible :...
}

沒法支持的特性:因爲ES5的侷限,Proxies沒法被編譯或polyfilled.詳見JavaScript引擎支持

符號 Symbols

符號可以實現對對象狀態的控制。字符串(與ES5中一致)與符號都能做爲鍵來訪問屬性。符號是一種新的原始類型。可選的名稱參數能夠用於調試,但並不是特性的一部分。符號是獨一無二的(就像gensym生成的同樣)但並不是是私有的,由於能夠用諸如Object.getOwnPropertySymbols這樣的方法使它暴露出來。

(function() {

  // 模塊做用域符號
  var key = Symbol("key");

  function MyClass(privateData) {
    this[key] = privateData;
  }

  MyClass.prototype = {
    doStuff: function() {
      ... this[key] ...
    }
  };

  // 在Babel中部分支持,全支持須要本地環境的實現
  typeof key === "symbol"
})();

var c = new MyClass("hello")
c["key"] === undefined

經過polyfill實現有限支持:有限支持須要Babel polypill。因爲語言的侷限,一些特性沒法被編譯或polyfilled。查閱core.js's caveats部分得到更多細節.

子類的構建 Subclassable Built-ins

在ES2015中像Array,Date和DOM元素均可以被繼承來構建子類。

// 數組子類的用戶代碼
class MyArray extends Array {
    constructor(...args) { super(...args); }
}

var arr = new MyArray();
arr[1] = 12;
arr.length == 2

Math + Number + String + Object APIs

許多新加入的庫,包括核心數學工具庫,數組轉換助手與用於複製的 Object.assign

Number.EPSILON
Number.isInteger(Infinity) // false
Number.isNaN("NaN") // false

Math.acosh(3) // 1.762747174039086
Math.hypot(3, 4) // 5
Math.imul(Math.pow(2, 32) - 1, Math.pow(2, 32) - 2) // 2

"abcde".includes("cd") // true
"abc".repeat(3) // "abcabcabc"

Array.from(document.querySelectorAll("*")) // 返回一個真的數組
Array.of(1, 2, 3) // 與new Array(...)相似,但沒有單一參數的特殊表現。
[0, 0, 0].fill(7, 1) // [0,7,7]
[1,2,3].findIndex(x => x == 2) // 1
["a", "b", "c"].entries() // iterator [0, "a"], [1,"b"], [2,"c"]
["a", "b", "c"].keys() // iterator 0, 1, 2
["a", "b", "c"].values() // iterator "a", "b", "c"

Object.assign(Point, { origin: new Point(0,0) })

在polypill中有限的支持:這些APIs中大多數 都能經過Babel polypill實現支持。然而,某些對特性的支持因爲一些緣由被去掉了。(例如String.prototype.normalize須要一些額外的代碼來支持)詳情請參閱

二進制與八進制字面量 Binary and Octal Literals

添加兩種新的數字字面量格式來支持二進制(b)與十進制(o)。

0b111110111 === 503 // true
0o767 === 503 // true

僅支持字面量格式:Babel僅支持對0o767的轉換但並不支持Number("0o767")格式。

Promises

Promises是一個異步編程庫。 are a library for asynchronous programming. Promises是對那些未來可能被使用的值的第一類描述。Promises被使用在許多JavaScript庫中.

function timeout(duration = 0) {
    return new Promise((resolve, reject) => {
        setTimeout(resolve, duration);
    })
}

var p = timeout(1000).then(() => {
    return timeout(2000);
}).then(() => {
    throw new Error("hmm");
}).catch(err => {
    return Promise.all([timeout(100), timeout(200)]);
})

經過polyfill實現支持:爲了實現Promises你必須引用Babel polypill。

Reflect API

完整的Reflect API經過對象暴露運行級別的元操做。這其實是一種反代理的API的模式,並容許調用與代理陷阱中相同的元操做。所以,在實現代理方面特別有用。

var O = {a: 1};
Object.defineProperty(O, 'b', {value: 2});
O[Symbol('c')] = 3;

Reflect.ownKeys(O); // ['a', 'b', Symbol(c)]

function C(a, b){
  this.c = a + b;
}
var instance = Reflect.construct(C, [20, 22]);
instance.c; // 42

經過polyfill實現支持:爲了實現Reflect API你必須引用Babel polypill。

尾調用 Tail Calls

用在尾部的調用能保證棧不會無限地增加。使得遞歸算法在面對無限輸入時更加安全。

function factorial(n, acc = 1) {
    "use strict";
    if (n <= 1) return acc;
    return factorial(n - 1, n * acc);
}

// 棧在如今大多數實現中會溢出
// 但在ES2015中對任何輸入都是安全的
factorial(100000)

在Babel 6中暫時性移除:因爲其複雜性與全局支持尾調用產生的衝突,只有在明確本身指向尾部的遞歸方法時才能支持。因爲其餘一些bug被移除並將獲得重新實現。

相關文章
相關標籤/搜索