在线免费阅读:https://leanpub.com/understandinges6/read/
部分代码使用原书,代码版权归原书所有
- 块级绑定(Block Bindings)
- 字符串
- 正则
- 字符串模板(template strings)
- 标签模板(tagged templates)
- 函数
- 对象
- 解构(Destructuring)
- Symbols
- 生成器(Generators)
- 迭代器(Iterators)
- 类
- Promises
- 模块(Modules)
- 杂七杂八
块级绑定(Block Bindings)↑
let
块级{}中有效
同块级不可重复声明
没有变量提升
块级会形成暂时性死区(TDZ,Temporal Dead Zone)
const
基本和 let
相同,值不可修改
let
和const
最好不要在全局下使用
字符串↑
unicode 支持更好
新增部分函数,支持双字节
codePointAt
,双字节版的 charCodeAt
,得到字符 unicode
fromCodePoint
,双字节版的 fromCharCode
,从 unicode 得出字符
includes
,包含某字符串
startsWith
,以某字符串开始
endsWith
,以某字符串结束
repeat
,重复字符串
normalize
,unicode 正规化,举个例子:两个 unicode 字符合成一个
正则↑
新增标志 u
正则识别 unicode 字符
新增标志 y
sticky,部分浏览器早就实现了
字符串模板(template strings)↑
1 | let a = 1 |
标签模板(tagged templates)↑
1 | let a = 1 |
函数↑
默认参数
1 | function foo ( bar = 1 ) { |
剩余参数
1 | function foo ( bar, ...rest ) { // ✓ |
函数属性 name
各种例子
1 | function doSomething() { |
new.target
避免了很多使用 new
的坑
1 | function Foo () { |
块级函数
块级中可定义函数
箭头函数
this
, super
, arguments
和 new.target
的值都在定义函数时绑定而非运行时绑定
不可 new
不可改变 this
的值
没有 arguments
跟普通函数一样拥有 name 属性
1 | var foo = value => value; // input value, output value |
1 | // this 的绑定 |
立即调用函数表达式(Immediately-Invoked Function Expressions (IIFEs))
1 | let foo = function ( s ) { |
新增尾递归优化
对象↑
对象字面属性值简写(Property Initializer Shorthand)
1 | function foo ( text ) { |
对象方法简写(Method Initializer Shorthand)
1 | var foo = { |
计算属性名语法
对象的属性可以使用中括号 []
表示需要「被计算」,结果转换为字符串作为属性名使用。
1 | let a = function () {} |
Object.is()
和经典的 ===
几乎一样,区别在于:
1 | console.log( +0 === -0); // true |
Object.assign()
1 | Object.assign( target, ...source ) |
读取源对象可列举的、自身的属性,将其赋值到目标对象上,覆盖旧属性,并非通常意义的复制。
复制存取器属性
此小节查询 MDN 后补充上
使用 Object.getOwnPropertyDescriptor(source, key)
读取,使用 Object.defineProperties
定义。
属性允许重复定义
属性以最后一个定义的值为准
修改原型
Object.getPrototypeOf
,得到原型
Object.setPrototypeOf
,设置原型
super
用以访问对象的 prototype
解构(Destructuring)↑
1 | var {a, b: { c, d }} = c |
解构可以有默认值,但只会在需要的时候求值。
2ality 有更详细清晰的解释:
1 | let {prop: y=someFunc()} = someValue; |
Symbols(不知道如何翻译,是第七种原始类型)↑
1 | var foo = Symbol() |
Symbol( 'description' )
生成局部 Symbol,即使 description
相同生成的 Symbol 也不一样
Symbol.for( 'description' )
生成全局 Symbol,description
相同则 Symbol 相同
获取对象的 Symbol 数组
Object.getOwnPropertySymbols( object )
强制转换 Symbol 为 String
原书本节未完成
有名的预定义 Symbol
原书本节大部分未完成
生成器(Generators)↑
生成迭代器的函数
1 | function *createIterator() { |
迭代器(Iterators)↑
for-of 语法
数组、字符串、映射(Map)、集合(Set)和元素数组(NodeList)都可迭代(iterable),可使用 for-of 语法
得到内置迭代器
Symbol.iterator 指向得到迭代器的函数
1 | let values = [1, 2, 3]; |
自定义迭代器
1 | let collection = { |
对象、数组、映射、集合都具有的默认迭代器
ertries()
,返回键值对迭代器
keys()
,返回键迭代器
values()
,返回值迭代器
字符串迭代器
通过 []
的访问是 code unit 方式
通过迭代器则是字符方式(几乎是,某些 unicode 支持不足)
元素数组(NodeList)迭代器
返回的是数组中的单个元素
向迭代器传参数
1 | function *foo () { |
生成器使用 return 提前返回
1 | function *createIterator() { |
委托生成器
使用 yield *
1 | function *createNumberIterator() { |
可以 yield *"string"
,会调用字符串的默认迭代器
1 | function *foo () { |
异步任务调度
以下是书中的例子,写得并不好,变量 task
的管理容易出问题:
1 | var fs = require("fs"); |
类↑
类声明
1 | class foo { |
类的属性最好都在构造函数里面创建。
类声明本质上就是以前的函数声明,除了以下有所不同:
- 类声明不会像函数声明那样被提升
- 类内部的代码全部以
strict mode
运行 - 所有方法都是不可列举的,相当于使用了
Object.defineProperty()
- 不使用
new
会抛异常 - 以类名命名方法来覆盖类名会抛异常(类名对于类内部来说是以
const
定义的,对于外部则不是)
类表达式
1 | let foo = class {} |
1 | let foo = class foo2 {} // foo === foo2 |
匿名类作为参数
1 | function createFoo ( c ) { |
立即调用类表达式(有点像立即调用函数表达式)
1 | let foo = new class { |
存取器属性
1 | class foo { |
静态成员
1 | class foo { |
静态成员同样不可列举
派生类
比起 ECMAScript5,ECMAScript6 的派生方便了很多
1 | class Rectangle { |
在派生类的构造函数中,调用 super
是必须的。如果连构造函数都没有,则:
1 | class Square extends Rectangle { |
- 只能在派生类中用
super()
- 使用
this
前必先调用super()
来初始化this
- 只有在构造函数返回一个对象的时候才可以不用
super()
类方法
覆盖、隐藏父类方法
1 | class Square extends Rectangle { |
仍然可以使用 super
调用父类方法
1 | class Square extends Rectangle { |
类方法没有 [[Construct]]
这个内部方法,所以不能被 new
。(什么是[[Construct]]
)
静态成员
相当于 ES5 中定义在构造函数上的方法(注意不是定义在构造函数的原型上),派生类显然也能调用
extends 关键字后面可以使用表达式
除了 null
和生成器函数外
1 | // 使用函数 |
new.target
能够得知类的调用状态,应用例如:阻止抽象类被实例化
1 | class Shape { |
Promises↑
Promise 是老朋友了,所以没有什么好记录的,就记一下语法。
1 | let p1 = new Promise( function ( resolve, reject ) { |
模块(Modules)↑
注:本章的代码似乎有一些问题,基本参考 MDN 为准
- 模块中的代码自动以严格模式运行
- 模块中的顶层变量只是模块中顶层,并非全局顶层
- 顶层中的
this
的值为undefined
- 代码中不允许 HTML 风格的注释
- 模块必须有导出的东西
基本导入导出
直接使用原书代码:
1 | // 导出数据 |
- 除非使用
default
语法,否则函数和类都不能使用匿名 export
只能用在顶层中
as
和 default
语法的情况,给出一个来自 2ality 的表格
Statement | Local name | Export name |
---|---|---|
export {v as x}; | ‘v’ | ‘x’ |
export default function f() {} | ‘f’ | ‘default’ |
export default function () {} | ‘default‘ | ‘default’ |
export default 123; | ‘default‘ | ‘default’ |
可以看出,所谓的默认导出其实就是用了 default
作为名字罢了。
还能够将其他模块重新导出
Statement | Module | Import name | Export name |
---|---|---|---|
export {v} from ‘mod’; | ‘mod’ | ‘v’ | ‘v’ |
export {v as x} from ‘mod’; | ‘mod’ | ‘v’ | ‘x’ |
export * from ‘mod’; | ‘mod’ | ‘*’ | null |
导入有很多方法,基本使用到的其实只有几种,以下来自 MDN:
1 | import name from "module-name"; |
最后那种导入是相当于将代码执行了一次。通常可以用来做 polyfills
和 shims
。
杂七杂八↑
Number.isInteger
,判断整数
Number.isSafeInteger
,判断是否是有效整数
Math 中加入很多函数,例如双曲正弦、双曲余弦之类的