资讯专栏INFORMATION COLUMN

[译] ES6 学习笔记:关于 ES2015 特性的详细概述

leoperfect / 3331人阅读

摘要:将转换成常见的使用实现的基于迭代器的迭代。处停止迭代器基于鸭子模型接口这里使用语法仅仅为了说明问题使用支持为了使用迭代器属性需要引入。生成器是迭代器的子类,包含了附加的与。

原文地址:http://babeljs.io/docs/learn-...
本文基于Luke Hoban精妙的文章《es6features》,请把star献给他,你可以在此尝试这些特性REPL。

概述

ECMAScript 2015 是 ECMAScript 在2015年6月正式发布的一套标准。ES2015是对语言的一次富有意义的更新,也是自2009年ES5标准发布以来,对于该门语言第一次主要的更新。主流JS引擎正在逐步完善对该标准的支持。

查看ECMAScript 2015的详尽文档

ECMAScript 2015的新特性 箭头函数与词法 this (Arrows and Lexical This)

箭头函数通过 => 语法简化函数的书写。其与C#,Java 8和CoffeeScript中相关特性有着相似的语法。同时支持表达式和语句。与函数不同,箭头函数与上下文共享词法作用域中的this

(注:为函数指ES5中函数)

// 表达式
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调用,实例与静态方法,构造函数。

(注:ES5中通过原型链模式来实现其他语言中通过类实现的逻辑,这种模式可以看作一种类模式)

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调用。总之,这使得对象与类的联系更加紧密,也让基于对象的设计从中得到了同样的便利。

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 "
" 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

默认参数 Default,Rest参数 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
Let与Const

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被移除并将得到从新实现。

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/80130.html

相关文章

  • JS笔记

    摘要:从最开始的到封装后的都在试图解决异步编程过程中的问题。为了让编程更美好,我们就需要引入来降低异步编程的复杂性。异步编程入门的全称是前端经典面试题从输入到页面加载发生了什么这是一篇开发的科普类文章,涉及到优化等多个方面。 TypeScript 入门教程 从 JavaScript 程序员的角度总结思考,循序渐进的理解 TypeScript。 网络基础知识之 HTTP 协议 详细介绍 HTT...

    rottengeek 评论0 收藏0
  • 2017-07-07 前端日报

    摘要:前端日报精选了解中的全局对象和全局作用域张鑫旭鑫空间鑫生活子进程你应该知道的一切直出内存泄露问题的追查实践我他喵的到底要怎样才能在生产环境中用上模块化腾讯前端大会大咖说大咖干货,不再错过发布发布中文翻译在使用进行本地开发代码 2017-07-07 前端日报 精选 了解JS中的全局对象window.self和全局作用域self « 张鑫旭-鑫空间-鑫生活Node.js 子进程:你应该知道...

    import. 评论0 收藏0
  • [] 前端攻略-从路人甲到英雄无敌二:JavaScript 与不断演化框架

    摘要:一般来说,声明式编程关注于发生了啥,而命令式则同时关注与咋发生的。声明式编程可以较好地解决这个问题,刚才提到的比较麻烦的元素选择这个动作可以交托给框架或者库区处理,这样就能让开发者专注于发生了啥,这里推荐一波与。 本文翻译自FreeCodeCamp的from-zero-to-front-end-hero-part。 继续译者的废话,这篇文章是前端攻略-从路人甲到英雄无敌的下半部分,在...

    roadtogeek 评论0 收藏0
  • []JavaScript ES6箭头函数指南

    摘要:以下例子的目的是使用来展示一个每秒都会更新的时钟当尝试在的回调中使用来引用元素时,很不幸,我们得到的只是一个属于回调函数自身上下文的。 前言 胖箭头函数(Fat arrow functions),又称箭头函数,是一个来自ECMAScript 2015(又称ES6)的全新特性。有传闻说,箭头函数的语法=>,是受到了CoffeeScript 的影响,并且它与CoffeeScript中的=>...

    makeFoxPlay 评论0 收藏0
  • 性能优化

    摘要:如果你的运行缓慢,你可以考虑是否能优化请求,减少对的操作,尽量少的操,或者牺牲其它的来换取性能。在认识描述这些核心元素的过程中,我们也会分享一些当我们构建的时候遵守的一些经验规则,一个应用应该保持健壮和高性能来维持竞争力。 一个开源的前端错误收集工具 frontend-tracker,你值得收藏~ 蒲公英团队最近开发了一款前端错误收集工具,名叫 frontend-tracker ,这款...

    liangzai_cool 评论0 收藏0

发表评论

0条评论

最新活动
阅读需要支付1元查看
<