This page looks best with JavaScript enabled

【译】ES6的350个要点概述

 ·  ☕ 21 min read

前言:

我的深入ES6系列由24篇覆盖了未来ES6中大多数语法以及特性的改变的文章组成。这篇文章的目的是总结所有的要点,提供给你ES6中最切实可行的建议,从而让你可以快速开始。我已经在深入ES6系列文章中提供链接,因此你可以轻易地深入到你感兴趣的主题。

听说你们喜欢要点,所以我写了这篇包含数百要点的文章。在开篇这里有覆盖所有主题的目录。显然,里面有你要的要点。需要注意的是,如果你想要让这些概念深入脑海的话,你将需要很多的时间去通过深入系列学习相关概念同时还需要练习,试验这写ES6的代码。

目录

十分抱歉目录这么长,下面我们开始ES6旅程。

介绍

  • ES6 - 也叫被称作“和谐”,“es-next”,“ES2015” - 是这门语言最新且已敲定的版本。
  • ES6规范最终敲定于2015年的7月(又称:ES2015)
  • 未来敲定的版本将会使用ES[YYYY]的命名模式,例如:ES2016对应于ES7
    • 每年发布一个新版本,新特性将会一波接着一波
    • 从ES6最先讨论中,我们大多数还是叫它ES6
    • 从ES2016开始(ES7)我们应该使用ES[YYYY]的模式来称呼新版本
    • 命名方案的最主要原因是想要迫使浏览器厂商尽快更新新的特性到现代浏览器中

工具

  • 要想让ES6运行,你需要一个JavaScript到JavaScript的转译器
  • 转译器具有下列功能
    • 它们允许你将相应语言的最新版本的代码翻译成旧版本的可运行代码
    • 随着浏览器的支持越来越好,我们会将ES2016和ES2017翻译成ES6以及以外版本的代码
    • 我们将会需要更好的来源映射功能
    • 它们是在产品上运行ES6版本语言的最可信任的方式(即使浏览器只支持ES5版本的语言)
  • Babel(一个转译器)有一个杀手级特性:阅读友好型输出
  • Babel去将ES6静态转译为ES5
  • babelifybabel合并到你的[Gulp, Grunt, or npm run][4ef399d4]构建流程当中去
  • Node.jsv4.x.x或者更高版本已经对ES6特性有了像样的支持,感谢V8
  • 在任何node的版本中使用babel-node,因为它可以将模块转译成ES5
  • Babel有蓬勃发展的生态系统,已经支持一些ES2016的特性,同时出了插件进行支持
  • 阅读ES6工具简史

解构赋值

  • var {foo} = pony等于var foo = pony.foo
  • var {foo: baz} = pony等于var baz = pony.foo
  • 你可以提供默认值,var {foo='bar'} = baz相当于foo: 'bar'如果baz.fooundefined
  • 只要你喜欢,你可以拿到尽可能多的属性,可以起别名或者不起
    • var { foo, bar:baz } = { foo: 0, bar: 1}将会得到foo:0 跟baz:1
  • 你可以用多层结构。var {foo: {bar}} = {foo: {bar: 'baz'}}会得到deep: 'baz'
  • 你也可以重命名。var {foo: {bar: deep}} = {foo: {bar: 'baz'}},会得到deep: 'baz'
  • 如果属性没有找到会像平时一样得到undefined,例如:var {foo} = {}
  • 多层结构中没有找到属性的话会抛出一个错误,例如:var {foo: {bar}} = {}
  • 对于数组一样适用,[a, b] = [0, 1]得到a: 0和b: 1
  • 你可以跳过数组中的元素,[a, , b] = [0, 1, 2],会得到a: 0和b: 2
  • 你可以交换两个数而不需要额外的交换参数,[a, b] = [b, a]
  • 你可以在函数的参数中使用解构赋值的特性
    • 分配默认参数如:function foo (bar=2) {}
    • 默认参数也可以是对象如:function foo (bar={a: 1, b: 2}) {}
    • 完全解构对象如:function foo({a = 1, b = 2}) {}
    • 如果额外的参数没有赋予任何值则提供一个空对象如:function foo ({a = 1, b = 2} = {}) {}
  • 深入了解JavaScript ES6解构

展开运算符以及不定参数

  • 不定参数是更好的arguments
    • 你应在方法签名中定义它如:function foo (...everything) {}
    • everything是一个传输所有参数到foo函数的数组
    • 你可以在...everything前定义一些参数如:function foo(bar, ...rest) {}
    • 已命名的参数将被排除在...rest之外
    • ...rest一定要是列表中的最后一个参数
  • 展开运算符比魔法更好用,也是一样使用...标记语法
    • 避免在.apply调用方法时使用,如:fn(...[1, 2, 3])fn(1, 2, 3)一样
    • 更容易的级联[1, 2, ...[3, 4, 5], 6, 7]
    • 将类数组对象或可迭代对象投射到数组中,如:[...document.querySelectorAll('img')]
    • 解构 中也十分好用,[a, , ...rest] = [1, 2, 3, 4, 5]会得到a: 1rest: [3, 4, 5]
    • new + .apply毫不费力,new Date(...[2015, 31, 8])
  • 深入ES6展开运算符

箭头函数

  • 能够用简易的方法来声明一个函数像param => returnValue
  • 在进行这些操作如:[1, 2].mpa(x => x * 2)将变得十分方便
  • 有几种方式可供选择,可能需要改掉以前的一些习惯
    • p1 => expr对于只有一个参数的函数是可行的
    • p1 => expr会隐含地返回所提供的expr表达式
    • 要隐式地返回一个对象可以将其包裹在一个括号() => ({foo: 'bar'})里面否则会抛出错误
    • 当你有零个,两个或多个参数的时候是需要括号包裹的() => expr或者(p1, p2) => expr
    • 在右边通过使用大括号可以声明一个代码块() => {},可以在里面写多句代码
    • 当使用代码块的方式的时候是没有隐式的返回操作的,需要自己主动提供() => {return 'foo'}
  • 你不可以静态地声明一个箭头函数,但是运行时对大多数方法都能够很好地推断出其名称
  • 箭头函数内的词法域绑定在其父级
    • this跟父级的this是一样的
    • this无法通过.call.apply或者是类似于反射类型的方法修改
  • 深入ES6箭头函数

模板字符串

  • 你可以通过” ` “反引号来定义字符串,而不是"或者'
  • 被反引号包裹的字符串称为模板字符串
  • 模板字符串可以是多行的
  • 模板字符串在里面进行插值操作如:poofoo.com is ${rating},里面的rating是一个变量
  • 你可以在插值的地方使用任何有意义的JavaScript表达式如:${ 2 * 3 }或者${foo()}
  • 你可以通过标签模板来修改模板字符串的插值方式
    • 在模板字符串前添加一个fn前缀fn`foo, ${bar} and ${baz}`
    • fn函数会被立马调用,传进去的参数是template, ...expressions
    • template实际上是['foo', ' and ', '']expressions则是[bar, baz]
    • fn返回的结果将会是模板函数的值
    • 有可能用于输入清理或者参数结构等地方
  • 将字符串包裹在模板字符串中比用单引号或双引号来包裹字符要好
  • 深入ES6模板字符串

对象字面值

  • 不用像之前那样{ foo: foo },现在可以直接{ foo },这被称作属性值速记
  • 属性名可以通过计算得到{[prefix + 'Foo']: 'bar'},如果prefix: 'moz',则会得到{ mozFoo: 'bar'}
  • 不可以同时使用属性值速记法与属性名计算,{[foo]}是无效的
  • 对象内的方法是常量,可以用选择更加简约的方式来定义,{ foo () {} }
  • 对象章节
  • 深入ES6对象字面值特性

  • 不是传统意义上的类,只是原型继承的语法糖
  • 语法跟一般的类定义相似,class Foo {}
  • 实例方法-new Foo().bar通过短对象字面值语法来定义,class Foo { bar() {}}
  • 静态方法-Foo.isPonyFoo()需要使用static前缀关键字,class Foo { static isPonyFoo() {} }
  • 构造函数方法class Foo{ constructor () { /* initialize instance */ } }
  • 通过简单的语法进行原型继承class PonyFoo extends Foo {}
  • 深入ES6类

Let以及Const

  • 在定义变量的时候可以选用let或者const来代替传统的var
  • let定义的变量是以块作用域的为范围的,而不是以function作为词法范围的
  • let的变量提升是在块内的,而var定义的变量提升是在函数内的提升
  • “暂时性死区” - 简称TDZ
    • 在块区中let foo被定义了之后开始产生
    • 结束于let foo声明被放置的用户代码区域(在里面跟变量提升没有关系)
    • 在TDZ中对foo(可以进行访问前)进行访问或分配值将会得到一个错误如:let foo = (() => foo = ‘test’)();
    • 有助于避免在一个变量被声明期间就进行操作而出现的神奇的bug
  • const也是块级作用域,有变量提升,也具有TDZ限制
  • const变量一定要在声明之初就初始化,const foo = 'bar'
  • const初始化之后再次定义的话会默默地失败而没有任何提示,(或者在严格模式下会抛出错误)
  • const一个变量不意味着分配的值不可变
    • const foo = { bar: 'baz' }意味着foo会总是引用右边的对象
    • const foo = { bar: 'bar' }; foo.bar = 'boo'不会抛出错误
  • 声明一个同样名字的变量会抛出错误
  • 为了修正在传输到某处时重新分配变量而丢失引用带来的错误
  • 在ES6,函数是块级作用域的
    • 防止通过变量提升来泄露块级作用域的内容{let _foo = 'secret', bar = () => _foo;}
    • 不会破坏通常情况下的用户的代码逻辑,以及常用的用法
  • 深入ES6的Let,Const和”暂时性死区”(TDZ)

Symbols

  • ES6中新的原始类型
  • 你可以这样var symbol = Symbol()创建你自己的Symbols
  • 你可以添加一个描述用于调试目的像:Symbol('ponyfoo')
  • Symbols是不可修改而且唯一的,Symbol(), Symbol(), Symbol('foo') Symbol('foo')彼此之间都是不一样的
  • Symbols是symbol类型,因此typeof Symbol() === 'symbol'
  • 你也可以通过Symbol.for(key)创建一个全局的Symbols
    • 如果一个具有key的symbol已经存在,就会直接返回这个Symbol
    • 否则,就会创建一个用key描述的新的Symbol
    • Symbol.keyFor(symbol)是一个反向操作的函数,通过传进去一个symbol得到一个返回值key
    • 全局symbols是全局可获得的,或者说是跨域的。单一的注册表用于在运行时访问这些symbols
      • window上下文
      • eval上下文
      • <iframe>上下文,Symbol.for('foo') === iframe.contentWindow.Symbol.for('foo')
  • 下面的也是知名的符号对象
    • 不在全局的注册表,通过Symbol[name]访问如:Symbol.iterator
    • 跨域,意味着Symbol.iterator === iframe.contentWindow.Symbol.iterator
    • 通过规范来定义协议如:iterable protocol对于Symbol.iterator
    • 它们实际上在口头术语上不是总所周知的
  • 对symbol对象的属性进行迭代是困难的,但并非是不可能的也绝不是私有的
    • Symbols对于所有的预ES6“反射”方法都是隐藏的
    • Symbols能够通过Object.getOwnPropertySymbols来访问
    • 你不会偶然找到它们,但是如果你积极寻找,就能找到它们
  • 深入ES6 Symbols 对象

迭代器

  • 迭代器和迭代规则定义了如何去遍历一个对象,不仅仅局限于数组或者类数组对象
  • 一个总所周知的是Symbol对象用于将迭代器分配到任何对象当中
  • var foo = { [Symbol.iterator]: iterable }或者foo[Symbol.iterator] = iterable
  • iterable是指具有返回一个具有next方法的iterator对象的方法
  • next方法返回一个具有两个属性的对象,分别是valuedone
    • value属性指示序列中遍历到的当前值
    • done方法指示是否还有更多的遍历对象要遍历
  • 一个具有[Symbol.iterator]值的对象是可遍历的,因为它们遵从迭代协议
  • 一些内建的对象如:Array,String或者arguments以及在浏览器中的NodeList在ES6中也是默认可迭代的
  • 可迭代对象能够通过for...of方法遍历,如:for (let el of document.querySelectorAll('a'))
  • 可迭代对象可以融合使用展开运算符像:[...document.querySelectorAll('a')]
  • 你也可以用Array.from(document.querySelectorAll('a'))去将一个可迭代序列融合进数组中
  • 迭代器是懒惰的,因此创建一个无限序列在程序中也是有效的
  • 需要注意的是不要对无限序列进行展开操作...或者用Array.from,因为这样会导致无限循环
  • 深入ES6迭代器

生成器

  • 生成器函数是一个特殊的迭代器,能够用这样的语法来声明function* generator () {}
  • 生成器函数用yield来返回一个元素序列
  • 生成器函数也能够用yield*去委托其他的生成器函数或者任何可迭代对象
  • 生成器函数返回一个同时具有可迭代性以及遵从迭代协议的生成器对象
    • 给定g = generator(),g遵从可迭代协议,因为g[Symbol.iterator]是里面的一个方法
    • 给定g = generator(),g遵从可迭代协议,因为g.next是一个方法
    • 迭代器对生成器对象g而言就是生成器本身:g[Symbol.iterator]() === g
  • 通过Array.from(g), [...g], for (let item of g)或者仅仅是用g.next()方法获取迭代器对象的值
  • 生成器函数的执行是暂停的并且记住最后的位置,有四种不同的情况
    • yield表达式返回序列的下一个值
    • return声明返回序列的最后一个值
    • throw声明会完全暂停生成器
    • 生成器函数在最后会发出信号{done: true}
  • 一旦g序列结束g.next()指示返回{done: true}且没有副作用
  • 使用生成器能够使得异步流感觉起来像同步流
    • 由用户提供生成器函数
    • 当异步操作发生的时候用户代码会暂停
    • 调用g.next(),用户代码取消暂停操作
  • 深入ES6生成器

Promises

  • 遵从Promises/A+规范,在ES6标准化之前就已经被广泛实施(如:bluebird
  • Promises的表现得像是一棵树。通过p.then(handler)p.catch(handler)来添加枝干
  • new Promise((resolve, reject) => { /* resolve */ })来创建一个新的Promises对象p
    • resolve(value)回调函数会用提供的value完成promise
    • reject(reason)回调函数会以reason错误来拒绝完成p
    • 你可以异步地来调用这些方法,在promise树的更深的层次里面进行阻塞
  • 每一次调用p.thenp.catch就会创建另外一个promise用于继续为已经成为完成态的p继续提供阻塞操作
  • Promises开始时是一个等待状态,在__fulfilled__或者__rejected__之后变成完成态
  • Promises可以只完成一次,然后它就变成完成态,变成完成态的promises会停止阻塞深层的分支
  • 你可以添加任意的promises到你需要添加的任意的分支上
  • 每一个分支都会执行.then处理函数或者.catch处理函数两者中的一个,且只能是一个
  • .then回调函数可以通过返回一个值来传送前一分支的结果
  • .then可以返回另外的一个阻塞的promises对象
  • p.catch(fn).catch(fn)不会像你预期的那样工作-除非你是想要捕捉错误处理器的错误
  • Promise.resolve(value)创建一个已经处于完成态且提供值的promise对象
  • Promise.reject(reason)创建一个已经处于失败态且提供原因的promise对象
  • Promise.all(...promises)创建一个等待所有...promises变成完成态或者其中一个变成失败态就变成稳定态的promise对象
  • Promise.race(...promises)创建一个只要...promises中有一个处于稳定状态的变成稳定态的promise对象
  • 使用Promises–一个可视化的promise练习场–去更好地理解Promises
  • 深入ES6的Promises

映射

  • 用于代替常见的用纯JavaScript对象实现的哈希映射
    • 避免用户提供的键带来的安全问题
    • 允许键为任意值,你甚至可以使用DOM元素或者函数作为条目的键
  • Map对象遵从迭代协议
  • new Map()方法来创建一个map对象
  • 像初始化一个iterable对象这样[[key1, value], [key2, value2]]来在new Map(iterable)里初始化一个map对象
  • map.set(key, value)来添加一个条目
  • map.get(key)来获取一个条目
  • 通过map.has(key)来用key作检查
  • map.delete(key)来删除一个条目
  • for (let [key, value] of map)来迭代一个map对象,要想展开map可以使用Array.from
  • 深入ES6的Maps对象

弱映射

  • 跟Map对象差不多,但是不完全一样
  • WeakMap不可迭代,所以你无法获取到像.forEach.clear等能在Map对象中获取到的列举方法
  • WeakMap的键一定要是引用类型,不可以使用像symbols,numbers或者strings等值类型作为键
  • WeakMapkey为键的条目对变量进行引用,其中引用的变量会被垃圾回收处理
  • 最后的点意味着WeakMap十分适合维持还在使用的对象的元数据(metadata)
  • 避免了内存泄露,无需进行手动引用计数可以将WeakMap想象成.NET中的IDisposable
  • 深入ES6弱映射

集合

  • Map很像,但不完全一样
  • Set没有键,只有值
  • set.set(value)看起来不是很好,所以使用set.add(value)作为替代
  • Sets不能有重复的值,因为值同时也是键
  • 深入ES6的集合类型

弱集合

  • WeakSet就像是SetWeakMap的杂交版本
  • WeakSet就是一个无法迭代,也没有列举方法的集合对象
  • WeakSet的值必须是一个引用类型
  • WeakSet在元数据表说明中会很有用,无论这个表中的引用是否一直活跃。
  • 深入ES6弱集合

代理

  • 代理对象用new Proxy(target, handler),target目标对象可以是任何对象,handler则是处理器对象
  • proxy的默认行为作为直通底层target对象的对象
  • Handlers定义底层对象target在常规对象之上的访问语法
  • 你将引用向下传递给proxy同时对target对象能如何与之交互保持绝对的掌控
  • Handlers也被称为Traps,这些术语经常互用
  • 你可以用Proxy.revocable(target, handler)创建一个可撤销的proxies对象
    • 这个方法返回一个拥有proxyrevoke属性的对象
    • 你可以方便地进行解构var {proxy, revoke} = Proxy.revocable(target, handler)
    • 你可以用new Proxy(target, handler)proxy进行相同的配置
    • 在调用revoke()之后,proxy对任何操作都会抛出错误,方便对不信任的使用者进行操作
  • get-相当于proxy.prop以及proxy['prop']
  • set-相当于proxy.prop = value以及proxy['prop'] = value
  • has-相当于in操作符
  • deleteProperty-相当于delete操作符
  • defineProperty-相当于Object.defineProperty和声明的替代方法
  • enumerate-相当于for..in循环
  • ownKeys-相当于Object.keys和相关方法
  • apply-相当于_function calls_
  • construct-相当于new操作符的用法
  • getPrototypeOf-相当于内部调用[[GetPrototypeOf]]
  • setPrototypeOf-相当于调用Object.setProrotypeOf
  • isExtensible-相当于调用Object.isExtensible
  • preventExtensions-相当于调用Object.preventExtensions
  • getOwnPropertyDescriptor-相当于调用Object.getOwnPropertyDescriptor
  • 深入ES6的Proxies
  • 深入ES6的Proxies Traps
  • 更多深入ES6的Proxies Traps

反射

  • Reflection是ES6中新的静态内置对象(想一下Math
  • Reflection方法有合理的内部结构,如:Reflection.defineProperty返回一个boolean值而不是抛出错误
  • 每个代理对象trap的handler都具有一个Reflection方法,代表每一个trap的默认行为
  • 更深入地,新的反射方法中同样的Object.keys会被放置在Reflection命名空间中
  • 深入ES6反射对象

Number对象

  • 使用0b作为二进制数前缀,0o作为八进制前缀
  • Number.isNaNNumber.isFinite除了不需要Number前缀以外,其他跟同名的全局变量一样。
  • Number.parseIntNumber.parseFloat跟它们的全局变量一模一样。
  • Number.isInteger检查输入的数值是否没有小数部分
  • Number.EPSILON帮忙区分两数相加跟确定值之间的微小差异如:0.1 + 0.2 和 0.3之间的差异
  • Number.MAX_SAFE_INTEGER代表刚好能在JavaScript中安全显示的最大的整数
  • Number.MIN_SAFE_INTEGER代表刚好能在JavaScript中安全显示的最小的整数
  • Number.isSafeInteger检查一个整数是否处于界限内,能够安全精确地显示
  • 深入ES6中改进的Number对象

Math对象

  • Math.sign对一个数进行sign运算的函数
  • Math.trunc求一个数的整数部分的函数
  • Math.cbrt求一个数的立方根的函数即:∛‾的值
  • Math.expm1求e的value次方减一的值,或者是evalue - 1的值
  • Math.log1p计算value+1的自然对数的值或者说ln(value + 1)
  • Math.log10计算以10为底的对数的值或者说log10(value)
  • Math.log2计算以2为底的对数的值或者说log2(value)
  • Math.sinh计算一个数的双曲正弦的值
  • Math.cosh计算一个数的双曲余弦的值
  • Math.tanh计算一个数的双曲正切的值
  • Math.asinh计算一个数的双曲反正弦的值
  • Math.acosh计算一个数的双曲反余弦的值
  • Math.atanh计算一个数的双曲反正切的值
  • Math.hypot计算两个数的平方和相加后求其平方根的值
  • Math.clz32计算并返回一个数字在转换成 32 无符号整形数字的二进制形式后, 开头的 0 的个数, 比如 1000000 转换成 32 位无符号整形数字的二进制形式后是 00000000000011110100001001000000, 开头的 0 的个数是 12 个, 则 Math.clz32(1000000) 返回 12
  • Math.imul返回两个参数的类C的32位整数乘法运算的运算结果
  • Math.fround将任意的数字转换为离它最近的单精度浮点数形式的数字
  • 深入ES6附加方法

Array对象

Object对象

字符串跟Unicode

模块组件

  • Strict Mode在ES6的模块系统中是默认开启的
  • ES6模块是一个export一个API的文件
  • export default value输出默认绑定的API
  • export var foo = ‘bar’输出一个命名绑定
  • 输出绑定的命名在模块输出的任何时候都可以改变它们
  • export { foo, bar }输出一列命名
  • export { foo as ponyfoo }将命名另起别名后输出
  • export { foo as default }将命名作为默认输出
  • 在所有模块的最后export default api最佳实践,其中为避免混乱,api是一个对象
  • 模块加载是一个特定的实现,允许跟CommonJS进行互相操作
  • import ‘foo’foo模块加载到当前模块当中
  • import foo from ‘ponyfoo’ponyfoo模块的默认输出分配给本地变量foo
  • import {foo, bar} from ‘baz’baz模块中加载输出的命名foobar
  • import {foo as bar} from 'baz'baz模块中加载foo并重命名为bar
  • import {default} from 'foo'也是加载默认输出
  • import {default as bar} from 'foo'加载默认输出并重命名为bar
  • import foo, {bar, baz} from 'foo'将默认输出foo以及命名输出barbaz混合在一起进行声明
  • import * as foo from ‘foo’加载命名空间对象
    • foo[name]包含所有的命名输出
    • 如果在模块中定义有默认输出的话,在foo[default]中包含默认输出
  • 深入ES6的模块拓展

到这里要点就结束了。话又说回来,我要提醒下你记得去阅读系列文章。不要忘记订阅或者甚至捐助Pony Foo,哦,还有,你尝试了绝技了吗?

翻译自PONYFOO.COM原文

Share on

Will
WRITTEN BY
Will
Web Developer