【第1006期】ECMAScript 6 新特性
前言
ES8 都提出来了,今天给我看 ES6 会不会呵呵呢?
今日早读文章由 @ 池盛星 翻译分享。
正文从这开始~
ECMAScript 6, 也就是ECMAScript 2015, 是最新版的ECMAScript标准. ES6是JavaScript语言的重要更新, 同时也是自2009年的ES5以来的第一次正式更新. 目前, 主要的JavaScript引擎正在实现这些功能.
要查看完整的ES6标准规范, 请点击ECMAScript 2015 Language Specification.
ES6包含以下新特性:
arrows
classes
enhanced object literals
template strings
destructuring
default + rest + spred
let + const
iterators + for..of
genetators
unicode
modules
module loaders
map + set + weakmap + weakset
proxies
symbols
subclassable built-ins
promises
math + number + string + array + object APIs
binary and octal literals
reflect api
tail calls
Arrows 箭头函数
Arrows是函数的一种缩写, 表现形式为=>. 它们在语法上, 类似于C#, Java 8以及CoffeeScript. 它们不仅支持块级函数体, 同时也支持带返回值的表达式函数体. 与普通函数的this作用域不同, 箭头函数内部与它周围代码共享this作用域. 即, this指向同一个东西 .
// Expression bodies 表达式函数体
let odds = evens.map(v => v + 1);
let nums = evens.map((v, i) => v + i);
let pairs = evens.map(v => ({even: v, odd: v + 1}));
// Statement bodies 块级函数体
nums.forEach(v => {
if (v % 5 === 0) {
fives.push(v);
}
});
// Lexical this: this 作用域
let bob = {
_name: "Bob",
_friends: [],
printFriends(){
this._friends.forEach(f => console.log(this._name + " knows " + f));
}
}
更多信息参考MDN: Arrow functions
Classes 类
ES6的类是基于prototype的OO模式的语法糖. 拥有简单方便的声明式格式, 使得类模式更易于使用, 并鼓励互操作性. 类支持基于prototype的继承, 超级调用(super calls), 实例(instance), 静态方法(static methods)以及构造函数 .
// Classes
class SkinnedMesh extends THREE.Mesh {
constructor(geometry, metrials) {
super(geometry, meterials);
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();
}
}
更多信息参考MDN: Classes
Enhanced Object Literals 增强的对象字面量
对象字面量被扩展用于支持在构建时设置prototype, foo: foo的简写, 定义方法, 超级调用(super calls)以及使用表达式计算属性名称. 同时, 这些也将对象字面量和类声明更紧密的结合在一起, 并使基于对象的设计获得相同的便利 .
let obj = {
// __proto__
__proto__: theProtoObj,
// Shorthand for `handler: handler`
handler,
// Methods
toString() {
// super calls
return "d " + super.toString();
},
// Computed (dynamic) property names
['prop_' + (()=> 42)]: 42
}
更多信息请参考MDN: Grammar and types: Object literals
Template Strings 模板字符串
模板字符串(template strings)提供了一个构建字符串的语法糖. 这有点类似于Perl, Python以及其它类似语言的字符串插入功能. 作为一种选择, 可以添加标签(tag)以允许字符串在构建时被定制, 避免注入攻击或者从字符串内容构建更高级别的数据结构 .
// 简单字面量字符串
`In JavaScript '\n' is a line-feed.`
// 多行字符串
`In JavaScript this is
not legal.`
// 字符串插值
let name = "Bob";
let time = "today";
`Hello ${name}, how are you ${time}?`
// 构造一个HTTP请求前缀, 用于演绎替换和构造
POST `http://foo.org/bar?a=${a}&b=${b}
Content-type: application/json
X-Credentials: ${credentials}
{"foo": ${foo},
"bar": ${bar}}`(myOnReadyStateChangeHandler);
更多信息请参考MDN: Template literals
Destructuring 解构
解构允许使用模式匹配进行绑定, 支持匹配数组和对象. 解构是故障弱化(指未匹配上时, 也不会出错, 而是产生一个undefined值), 类似于标准对象查找foo["bar"], 当未找到时, 产生一个undefined 值 .
// list匹配
let [a, , b] = [1, 2, 3];
// a=1; b=3
// object匹配
let {op: a, lhs: {op: b}, rhs: c} = getASTNode();
// object匹配简写
// 在scope内绑定`op`, `lhs`及`rhs`
let {op, lhs, rhs} = getASTNode();
// 可用于参数位置
function g({name: x}) {
console.log(x); // 5
}
g({name: 5});
// 故障弱化解构
let [a] = [];
a === undefined;
// 故障弱化解构, 带默认值
let [a = 1]=[];
a === 1;
更多信息请参考MDN: Destructuring assignment
Default + Rest + Spread 默认参数, 剩余参数(Rest Parameters), Spread语法
为函数的参数设置默认值. 将传入的参数的剩余参数绑定到数组. 使用...操作符, 将数组解构成参数列表 .
// 给参数设置默认值
function f(x, y = 12) {
// 如果没有传入y的值(或者传入的是undefined), 那么y就等于默认的值12
return x + y;
}
f(3) === 15
// 将传入的参数的剩余参数绑定到数组
function f(x, ...y) {
// y是一个数组Array
return x * y.length;
}
f(3, "hello", true) === 6;
// 使用...操作符, 将数组解构成参数列表
function f(x, y, z) {
return x + y + z;
}
// 传入一个数组作为参数
f(...[1, 2, 3]) === 6;
更多信息请参考MDN: Default parameters, Rest parameters, Spread syntax
Let + Const
用let和const代替var, 定义块级作用域的变量 .
function f() {
let x;
{
// 由于let定义的是块级作用域的变量, 所以这样是合法的
const x = "sneaky";
// 由于const定义的是常量, 所以不能再进行赋值操作
x = "foo";
}
// 由于在函数f()内, 前面已经有let x的定义, 所以不能再次定义
// 报错: Uncaught SyntaxError: Identifier 'x' has already been declared
let x = "inner";
}
更多信息请参考MDN: let, const
Iterators + for..of 迭代器 for..of
迭代器对象启用自定义迭代, 如CLR IEnumberable或Java Iterable. 通用于for..in基于自定义迭代器的迭代, for..of用法类似. 不需要是一个真正的数组, 像LINQ一样, 启用懒设计模式 .
let fibonacci = {
[Symbol.iterator](){
let pre = 0;
let 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);
}
迭代器对象启用自定义迭代,如CLR IEnumerable或Java Iterable。 通用于...的自定义基于迭代器的迭代。 不需要实现阵列,实现LINQ等懒人设计模式。
更多信息请参考MDN: for...of
Generators
Generators使用function* 和yield简化iterator-autoring. 声明为function*的函数返回一个Generator实例. Generators是迭代器的子类型, 包括next和throw. 这些可能的值返回到generator, 因此yield是一个表达式, 它返回值或throws.
注意: 同时也可以用来启用"await"类似的异步编程, 请参阅ES7await 提案 .
let fibonacci = {
[Symbol.iterator]: function*() {
let pre = 0;
let cur = 1;
for (; ;) {
let temp = pre;
pre = cur;
cur += temp;
yield cur;
}
}
}
for (let p of fibonacci) {
// 在1000处截断序列
if (n > 1000) {
break;
}
console.log(n);
}
使用generator接口:(使用TypeScript 语法)
interface Generator extends Iterator {
next(value?: any): IteratorResult;
throw(exception: any);
}
更多信息请参考MDN: Iteration protocols
Unicode
// same as ES5.1
"𠮷".length == 2
// new RegExp behaviour, opt-in ‘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);
}
更多信息请参考MDN: RegExp.prototype.unicode
Modules 模块
语言级支持模块化组件定义. 吸收流行的JavaScript模块加载方式(AMD, CommonJS). 由host-defined默认加载器定义运行时行为. 隐式异步模型 -- 在请求的模块可用和处理之前, 不执行任何代码 .
// lib/math.js
export function sum(x, y) {
return x + y;
}
export let 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 default和export * 的功能:
// lib/mathPlusPlus.js
export * from "lib/math";
export let e = 2.71828182846;
export default function (x) {
return Math.log(x);
}
// app.js
import ln, {pi, e} from "lib/mathPlusPlus";
console.log(`2π = ${ln(e) * pi * 2}`);
更多信息请参考MDN: import, export
Module Loaders 模块加载器
模块加载器支持:
Dynamic loading 动态加载
State isolation
Global namespace isolation
Complilation hooks
Nested virtualization
可用配置默认模块加载器, 并且可以构造新的加载器以在隔离或约束上下文中评估和加载代码 .
// Dynamic loading - "System" is default loader
System.import("lib/math").then(function(m) {
console.log(`2π = ${m.sum(m.pi, m.pi)}`);
});
// Create execution sandboxes - new Loaders
let loader = new Loader({
global: fixup(window) // replace "console.log"
});
loader.eval("console.log('hello world!');");
// Directly manipulate module cache
System.get('jquery');
System.set('jquery', Module({$: $})); // 警告: 还没有最终定稿
Map, Set, WeakMap, WeakSet
常用算法的高效数据结构. WeakMaps提供了无密钥(leak-free) object-key的side tables.
// Sets
let s = new Set();
s.add("hello").add("goodbye").add("hello");
// s.size === 2;
// s.has("hello") === true;
// Maps
let m = new Map();
m.set("hello", 42);
m.set(s, 34);
// m.get(s) === 34;
// Weak Sets
let ws = new WeakSet();
ws.add({data: 42});
// Weak Maps
let wm = new WeakMap();
wm.set(s, {extra: 42});
// wm.size === undefined;
更多信息请参考MDN: Map, Set, WeakMap, WeakSet
Proxies 代理
代理可以创建具有可用于托管对象的全部行为的对象 .
// Proxying a normal object
let target = {};
let handler = {
get: function(receiver, name) {
return `Hello, ${name}!`;
}
}
let p = new Proxy(target, handler);
// p.world === `Hello, world!`;
// Proxying a function object
let target = function() {return `I am the target`;};
let handler = {
apply: function(receiver, ...args) {
return `I am the proxy.`;
}
}
let p = new Proxy(target, handler);
// p() === `I am the proxy.`;
有可用于所有运行时元操作(meta-operations)的陷阱(traps):
var handler =
{
get:...,
set:...,
has:...,
deleteProperty:...,
apply:...,
construct:...,
getOwnPropertyDescriptor:...,
defineProperty:...,
getPrototypeOf:...,
setPrototypeOf:...,
enumerate:...,
ownKeys:...,
preventExtensions:...,
isExtensible:...
}
更多信息请参考MDN: MDN Proxy
Symbols 一种新的原始数据类型
Symbols允许对象状态的访问控制. Symbols允许属性是一个字符串(如ES5)或者symbol. Symbols是一种新的原始数据类型. 可选描述参数用于调试, 但不是身份的一部分. Symbols是唯一的(如gensym), 但不是私有的, 因为他们通过反射功能进行显示, 如Object.getOwnPropertySymbols.
let MyClass = (function () {
// module scoped symbol
let key = Symbol("key");
function MyClass(privateData) {
this[key] = privateData;
}
MyClass.prototype = {
doStuff: function () {
// ...
this[key]
// ...
}
}
return MyClass;
})();
let c = new MyClass("hello");
// c["key"] === undefined;
更多信息请参考MDN: Symbol
Subclassable Built-ins
在ES6中, 像Array, Date及DOM里面的Element都可以被子类化(subclassed).
名为Ctor的函数的对象构造现在使用两个阶段(两者都是实际派生的):
调用Ctor [@@create]分配对象,安装任何特殊行为
调用新实例上的构造函数进行初始化
已知的@@create symbol可通过Symbol.create获得。 内置函数现在显式公开其@@create。
// 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
let arr = new MyArray();
arr[1] = 12;
// arr.length === 2;
Math, Number, String, Array, Object APIs
新增了许多新的库, 包括核心的Math库, Array转换助手, String助手以及用于copy的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, 1]
[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)});
更多信息请参考MDN: Number, Math, Array.from(), Array.of(), Array.prototype.copyWithin(), Object.assign()
Binary and Octal Literals 二进制和八进制
新添加两个新的数字字面量格式: Binary(b)和Octal(o)
0b111110111 === 503; // true
0o767 === 503; // true
Promises
Promises是一个异步编程库. Promises是一个容器, 保存着某个未来才会结束的事件(通常是一个异步操作)的结果. Promises已经在现在存在的JavaScript库中被广泛的使用.
function timeout(duration = 0) {
return new Promise((resolve, reject) => {
setTimeout(resolve, duration);
});
}
let p = timeout(1000).then(()=> {
return timeout(2000);
}).then(()=> {
throw new Error("hmm");
}).catch(err=> {
return Promise.all([timeout(100), timeout(200)]);
})
更多信息请参考MDN: Promise
Reflect API
Reflect对象与Proxy对象一样,也是 ES6 为了操作对象而提供的新 API. 全反射API显示了运行时(runtime-level)对象的元操作(meta-operations). 这实际上是Proxy API的逆向, 并允许进行与代理陷阱(proxy traps)相同的元操作(meta-operations)的调用.
请参考MDN: Reflect
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
factorial(100000);
关于本文
译者:@池盛星
译文:https://zhuanlan.zhihu.com/p/27966346
作者:@lukehoban
原文:https://github.com/lukehoban/es6features