介紹
ES6,也叫ECMAScript2015(以下統稱ES6),是ECMAScript標准的最新版本。這個標准在2015年6月份被正式批准。ES6是js語言很有意義的一次更新,也是2009年ES5被標准化以來第一次重大的更新。主流javascript引擎中的這些新特性正在開發中。
ES6特性完整版參見:ES6特性完整版說明
ES6有以下這些新特性:
箭頭函數
使用=>表示function的簡略版。這和C#,Java8,CoffeeScript的語法很像。它可以支持聲明體和表達式體,並且表達式體會有一個返回值。不像普通函數,箭頭函數和其上下文共享同一個詞法作用域的this。
(譯者注:詞法作用域即“靜態作用域”,)
// Expression bodies 表達式體
var odds = evens.map(v => v + 1);
/*v=>v+1相當於function(v){return v+1;}*/
var nums = evens.map((v, i) => v + i);
var pairs = evens.map(v => ({even: v, odd: v + 1}));
// Statement bodies 聲明體
nums.forEach(v => {
if (v % 5 === 0)
fives.push(v);
});
/*相當於nums.forEach(function (v) {
if (v % 5 === 0) fives.push(v);
});*/
// Lexical this 詞法作用域的this
var bob = {
_name: "Bob",
_friends: [],
printFriends() {
this._friends.forEach(f =>
console.log(this._name + " knows " + f));
}
}
類
ES2015的classes是在基於原型的面向對象模式中的一個簡單的語法糖。它有一個簡單的聲明形式,使得類模式更易於使用,並且這種方式有益於互用性。classes支持基於原型的繼承、父類方法的調用、實例化和靜態方法、構造函數。
(譯者注:超級像后端語言。)
class SkinnedMesh extends THREE.Mesh {//使用extends來表示繼承
constructor(geometry, materials) {
super(geometry, materials);//父類的構造函數
this.idMatrix = SkinnedMesh.defaultMatrix();
this.bones = [];
this.boneMatrices = [];
//...
}
update(camera) {
//...
super.update();
}
get boneCount() {
return this.bones.length;
}
set matrixType(matrixType) {
this.idMatrix = SkinnedMesh[matrixType]();
}
static defaultMatrix() {//靜態方法
return new THREE.Matrix4();
}
}
增強的對象字面量
對象字面量被擴展成可以在構造階段設置原型、變量名和變量值相同時可以使用簡寫形式(如foo:foo 可以寫成foo)、定義方法、調用父方法、動態計算變量名。同時,這種方式也和上一小節說的類(class)聲明十分吻合,也使得基於對象的設計更加便利。
var obj = {
// __proto__ obj的原型
__proto__: theProtoObj,
// Shorthand for ‘handler: handler’ 簡寫,翻譯過來就是handler : handler,
handler,
// Methods
toString() {
// Super calls 調用父類的函數
return "d " + super.toString();
},
// Computed (dynamic) property names 需要計算的動態屬性名稱
[ 'prop_' + (() => 42)() ]: 42
};
字符串模板
字符串模板提供了一個創建字符串的語法糖。這和perl、python等等語言中的字符串插入功能很類似。有選擇性的,它允許在字符串中插入一個標簽(tag)使得字符串的構建是可定制化的,這可以避免在字符串里進行注入攻擊,或者構造更高級別的結構。
// Basic literal string creation 基本的字符串字面量方式創建
`In JavaScript '\n' is a line-feed.`
// Multiline strings 多行字符串
`In JavaScript this is
not legal.`
// String interpolation 插入變量
var name = "Bob", time = "today";
`Hello ${name}, how are you ${time}?`
// Construct an HTTP request prefix is used to interpret the replacements and construction
// 構建一個通用的http請求前綴,其中字段值可以動態替換
POST`http://foo.org/bar?a=${a}&b=${b}
Content-Type: application/json
X-Credentials: ${credentials}
{ "foo": ${foo},
"bar": ${bar}}`(myOnReadyStateChangeHandler);
解構(解析結構)
解構支持使用模式匹配來匹配數組和對象。結構是弱化失敗的,類似於在一個標准的對象中查找foo[“bar”]屬性,如果沒有找到只是返回undefined。
(譯者注:fail-soft-字面意思是弱化失敗,再這里是指如果解析結構解析不出來不會拋異常,只是返回一個undefined或默認值)
// list matching 匹配數組
var [a, , b] = [1,2,3];
// object matching 匹配對象
var { op: a, lhs: { op: b }, rhs: c }
= getASTNode()
// object matching shorthand 匹配對象的一部分
// binds `op`, `lhs` and `rhs` in scope 例如getASTNode()的返回值為{op,op2,lhs,rhs},這個表達式將會自動匹配op,lhs,rhs
/*稍微有點難懂,下面的語句翻譯過來就是
var _getASTNode = getASTNode();
var op = _getASTNode.op;
var lhs = _getASTNode.lhs;
var rhs = _getASTNode.rhs;
*/
var {op, lhs, rhs} = getASTNode()
// Can be used in parameter position 可以作為參數使用
function g({name: x}) {
console.log(x);
}
g({name: 5})
// Fail-soft destructuring 弱化解析失敗,返回undefined
var [a] = [];
a === undefined;
// Fail-soft destructuring with defaults 有默認值時弱化解析失敗,返回undefiend
var [a = 1] = [];
a === 1;
默認值+不定參數+參數展開
默認值:支持由被調用函數設置的參數默認值。
參數展開:在函數調用時實參使用...運算符,可以將作為參數的數組拆解為連續的多個參數。 在函數定義時虛參使用...運算符,則可以將函數尾部的多個參數綁定到一個數組中。
不定參數:不定參數取代了傳統的參數,並可更直接地應用於通常的用例中。
//默認值
function f(x, y=12) {
// y is 12 if not passed (or passed as undefined) 如果y沒有實參,則賦值為默認值12
return x + y;
}
f(3) == 15
//不定個數的參數
function f(x, ...y) {
// y is an Array 有...,表示y是一個數組
return x * y.length;
}
f(3, "hello", true) == 6
//參數展開
function f(x, y, z) {
return x + y + z;
}
// Pass each elem of array as argument ...表示在數組中的每一個元素分別按順序對應一個入參
f(...[1,2,3]) == 6
let和const關鍵字
兩個關鍵字都具有塊級作用域,只在塊級作用域中生效。let是一種新的var。(譯者注:let可以看成var,它定義的變量被限制在特定范圍中才能使用,離開這個范圍就自動銷毀)。const只能一次性聲明(譯者注:不能修改其值)。兩者靜態限制防止了在賦值前使用變量。
function f() {
{
let x;
{
// okay, block scoped name 不報錯
const x = "sneaky";
// error, const 報錯,常量不允許修改值
x = "foo";
}
// error, already declared in block 報錯,在該塊級作用域中已經聲明了
let x = "inner";
}
}
Iterator+For..Of 迭代器
可以像CLR IEnumerable或JAVA Utterable一樣自定義迭代器。將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) {
// truncate the sequence at 1000 實現從1到1000的斐波那契數列計算
if (n > 1000)
break;
console.log(n);
}
迭代器是基於這些鴨子類型的接口(使用TypeScript類型語法,僅僅用於闡述問題)
(譯者注:duck typing 鴨子類型。源自於James Whitcomb Riley提出的鴨子測試——“當看到一只鳥走起來像鴨子、游泳起來像鴨子、叫起來也像鴨子,那么這只鳥就可以被稱為鴨子。”再使用鴨子類型的語言中,若某函數定義是按照入參為鴨子類型的參數來編寫的,那么運行時接受的參數不管不是鴨子類型的,都會調用它游泳、叫得方法,如果調不到就會報錯。關注的不是對象的類型本身,而是它是如何使用的。js就支持鴨子類型。動態語言都醬紫。)
interface IteratorResult {
done: boolean;
value: any;
}
interface Iterator {
next(): IteratorResult;
}
interface Iterable {
[Symbol.iterator](): Iterator
}
Generators 生成器
Generators 通過使用function和yield簡化了迭代器編寫。函數聲明時如果是function 形式的會返回一個Generator實例。Generator是迭代器iterators的子類,還包括了額外的next和throw方法。這允許了值可以回流到生成器中,所以yield是返回一個值或者拋異常的一個表達式。
注意:也可以用作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);
}
接上篇
Unicode
新增加的特性不影響老功能的使用,字符串將有新的文本格式,也增加了正則 u 模式來處理碼位。同時,新的api可以在21碼位級別上處理字符串。在javascript中,這些新增點可以支持構建全球化應用。
// same as ES5.1
// 與 ES5.1 相同
"吉".length == 2
// new RegExp behaviour, opt-in ‘u’
// 使用可選的‘u’修飾符表示正則
"吉".match(/./u)[0].length == 2
// new form
// 左邊是新寫法,右邊是舊寫法。新的形式可以通過添加一組大括號`{}`來表示超過四字節的碼點
"\u{20BB7}"=="吉"=="\uD842\uDFB7"
// new String ops
"吉".codePointAt(0) == 0x20BB7
// for-of iterates code points
// 以碼位為單位進行迭代
for(var c of "吉") {
console.log(c);
}
"
模塊 modules
在語言層面上支持使用模塊來定義組件。將流行的javascript模塊加載器(AMD,CommonJS)的模式編成規范。運行時的行為由宿主定義的默認加載器決定。隱式異步模型- (當前模塊)沒有代碼會執行,直到被請求的模塊可用且處理過。
// lib/math.js
export function sum(x, y) { //export 表示可以被加載
return x + y;
}
export var pi = 3.141593;
// app.js
import * as math from "lib/math"; //import 表示被導入
alert("2π = " + math.sum(math.pi, math.pi));
// app.js
import * as math from "lib/math";
alert("2π = " + math.sum(math.pi, math.pi));
額外的新增的特征包括export default和export * ;
// lib/mathplusplus.js
export * from "lib/math";
export var e = 2.71828182846;
export default function(x) {//注意default關鍵字表示默認
return Math.log(x);
}
// app.js
import ln, {pi, e} from "lib/mathplusplus";
alert("2π = " + ln(e)*pi*2);
Map + Set + WeakMap + WeakSet
這四個對於常見的算法來說是很有用的數據結構。weakMaps這一數據結構提供的索引表,擁有不會發生內存泄露的對象鍵名。(注:指的是當對象作為鍵名時,對象可能會被回收,回收后自動移除所在鍵值對)。
// 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 });
// Because the added object has no other references, it will not be held in the set
//因為這個新的對象沒有其他引用,所以不會真的添加到set
Proxies 代理
代理創建的對象可以獲得宿主對象的全部行為(屬性和方法)。可以用於攔截,虛擬對象,日志/分析等。
// Proxying a normal object 代理一個普通對象
var target = {};//被代理的類
var handler = {//第二個參數表示攔截后的操作
get: function (receiver, name) {//這里是攔截獲取屬性的操作:p.world 相當於第二個參數name為world
return `Hello, ${name}!`;
}
};
var p = new Proxy(target, handler);
p.world === 'Hello, world!';
// Proxying a function object 代理一個函數對象
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 =
{
get:...,
set:...,
has:...,
deleteProperty:...,
apply:...,
construct:...,
getOwnPropertyDescriptor:...,
defineProperty:...,
getPrototypeOf:...,
setPrototypeOf:...,
enumerate:...,
ownKeys:...,
preventExtensions:...,
isExtensible:...
}
Symbols 符號
symbol有權限控制對象狀態。symbol允許通過String(與ES5相同)或symbol作為鍵來訪問對象的屬性。symbol是一個新的原始類型。可選的name參數用於調試-但是它不是符號身份的一部分。符號是唯一的(就像gensym),但是只要他們通過像Object.getOwnPropertySymbles這樣的反射特性暴露出來,就不是私有的。
var MyClass = (function() {
// module scoped symbol
var key = Symbol("key");
function MyClass(privateData) {
this[key] = privateData;
}
MyClass.prototype = {
doStuff: function() {
... this[key] ...
}
};
return MyClass;
})();
var c = new MyClass("hello")
c["key"] === undefined//已經跳出了作用域,所以是undefined
內建對象可擁有子類
在ES6中,像Array,Date和Dom元素這樣的內建對象都可以被子類化。
// Pseudo-code of Array
class Array {
constructor(...args) { /* ... */ }
static [Symbol.create]() {
// Install special [[DefineOwnProperty]]
// to magically update 'length'
}
}
// User code of Array subclass
class MyArray extends Array {
constructor(...args) { super(...args); }
}
// Two-phase 'new':
// 1) Call @@create to allocate object
// 2) Invoke constructor on new instance
/*
兩個階段的new:
1) 調用@@create來為對象分配空間
2)在新實例上調用構造函數
*/
var arr = new MyArray();
arr[1] = 12;
arr.length == 2
Math + Number + String + Object APIs
加了一些新的api,包括math的核心庫,數組轉換幫助函數,和用來復制對象的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('*')) // Returns a real Array
Array.of(1, 2, 3) // Similar to new Array(...), but without special one-arg behavior
[0, 0, 0].fill(7, 1) // [0,7,7]
[1, 2, 3].find(x => x == 3) // 3
[1, 2, 3].findIndex(x => x == 2) // 1
[1, 2, 3, 4, 5].copyWithin(3, 0) // [1, 2, 3, 1, 2]
["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) }) //拷貝對象
Binary and Octal Literals
增加了兩個新的數字進制標識符,第二個字母為b來表示二進制,第二個字母為o來表示八進制。
0b111110111 === 503 // true 二進制
0o767 === 503 // true 八進制
Promises
Promises是處理異步操作的一種模式。 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)]);
})
Reflect API
完整的反射API暴露了對象在運行時的元操作。這類似於一個反向代理,並允許調用與代理陷阱中相同的元操作。實現代理非常有用。
暫無實例
Tail Calls
保證尾部調用時棧區不會沒有限制的增長,這使得遞歸函數及時在沒有限制的輸入時也能保證安全性。
function factorial(n, acc = 1) {
'use strict';
if (n <= 1) return acc;
return factorial(n - 1, n * acc);
}
// Stack overflow in most implementations today, 棧溢出在現在經常存在
// but safe on arbitrary inputs in ES6 但是在ES6中卻很安全
factorial(100000)
附錄
- 原文鏈接
- babel在線編譯器,可以將大部分ES6新邏輯代碼實時編譯成ES5
常見英文詞匯
- pattern 模式
- literal 字面量
- lexical 詞法作用域
- surrounding code 上下文
- Object literals 對象字面量
- Rest 不定參數
- assignment 賦值