查看原文
其他

【第1006期】ECMAScript 6 新特性

2017-07-25 池盛星 前端早读课

前言

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

您可能也对以下帖子感兴趣

文章有问题?点此查看未经处理的缓存