From 911dd3404a663e6d9f064ccd16cad25c8c9abab7 Mon Sep 17 00:00:00 2001 From: ruanyf Date: Thu, 12 Oct 2017 09:59:24 +0800 Subject: [PATCH 001/595] =?UTF-8?q?docs(class-extends):=20=E5=AD=90?= =?UTF-8?q?=E7=B1=BB=E7=BB=A7=E6=89=BF=E7=88=B6=E7=B1=BB=E7=9A=84=E9=9D=99?= =?UTF-8?q?=E6=80=81=E6=96=B9=E6=B3=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/class-extends.md | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/docs/class-extends.md b/docs/class-extends.md index 02b36ee22..ead4ea8c3 100644 --- a/docs/class-extends.md +++ b/docs/class-extends.md @@ -92,6 +92,23 @@ cp instanceof Point // true 上面代码中,实例对象`cp`同时是`ColorPoint`和`Point`两个类的实例,这与 ES5 的行为完全一致。 +最后,父类的静态方法,也会被子类继承。 + +```javascript +class A { + static hello() { + console.log('hello world'); + } +} + +class B extends A { +} + +B.hello() // hello world +``` + +上面代码中,`hello()`是`A`类的静态方法,`B`继承`A`,也继承了`A`的静态方法。 + ## Object.getPrototypeOf() `Object.getPrototypeOf`方法可以用来从子类上获取父类。 From 0f0408563d62973ce89c812aeee7eea4a9e569b7 Mon Sep 17 00:00:00 2001 From: ruanyf Date: Thu, 12 Oct 2017 17:53:32 +0800 Subject: [PATCH 002/595] =?UTF-8?q?docs(module-loader):=20=E6=B5=8F?= =?UTF-8?q?=E8=A7=88=E5=99=A8=E6=A8=A1=E5=9D=97=E7=9A=84=E5=8A=A0=E8=BD=BD?= =?UTF-8?q?=E9=A1=BA=E5=BA=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/module-loader.md | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/docs/module-loader.md b/docs/module-loader.md index 664ebc58e..5f22f8db6 100644 --- a/docs/module-loader.md +++ b/docs/module-loader.md @@ -39,7 +39,7 @@ 浏览器加载 ES6 模块,也使用` + ``` 上面代码在网页中插入一个模块`foo.js`,由于`type`属性设为`module`,所以浏览器知道这是一个 ES6 模块。 @@ -47,17 +47,21 @@ 浏览器对于带有`type="module"`的` + - + ``` +如果网页有多个` + ``` +一旦使用了`async`属性,` ``` -上面代码中,一共有4个`script`标签。第一个是加载 Traceur 的库文件,第二个和第三个是将这个库文件用于浏览器环境,第四个则是加载用户脚本,这个脚本里面可以使用 ES6 代码。 +上面代码中,一共有 4 个`script`标签。第一个是加载 Traceur 的库文件,第二个和第三个是将这个库文件用于浏览器环境,第四个则是加载用户脚本,这个脚本里面可以使用 ES6 代码。 注意,第四个`script`标签的`type`属性的值是`module`,而不是`text/javascript`。这是 Traceur 编译器识别 ES6 代码的标志,编译器会自动将所有`type=module`的代码编译为 ES5,然后再交给浏览器执行。 @@ -495,7 +495,7 @@ Traceur 允许将 ES6 代码直接插入网页。首先,必须在网页头部 ``` -上面代码中,首先生成Traceur的全局对象`window.System`,然后`System.import`方法可以用来加载 ES6。加载的时候,需要传入一个配置对象`metadata`,该对象的`traceurOptions`属性可以配置支持 ES6 功能。如果设为`experimental: true`,就表示除了 ES6 以外,还支持一些实验性的新功能。 +上面代码中,首先生成 Traceur 的全局对象`window.System`,然后`System.import`方法可以用来加载 ES6。加载的时候,需要传入一个配置对象`metadata`,该对象的`traceurOptions`属性可以配置支持 ES6 功能。如果设为`experimental: true`,就表示除了 ES6 以外,还支持一些实验性的新功能。 ### 在线转换 @@ -586,4 +586,3 @@ fs.writeFileSync('out.js', result.js); // sourceMap 属性对应 map 文件 fs.writeFileSync('out.js.map', result.sourceMap); ``` - diff --git a/docs/iterator.md b/docs/iterator.md index 7a43ae1c9..67b883c14 100644 --- a/docs/iterator.md +++ b/docs/iterator.md @@ -4,9 +4,9 @@ JavaScript 原有的表示“集合”的数据结构,主要是数组(`Array`)和对象(`Object`),ES6 又添加了`Map`和`Set`。这样就有了四种数据集合,用户还可以组合使用它们,定义自己的数据结构,比如数组的成员是`Map`,`Map`的成员是对象。这样就需要一种统一的接口机制,来处理所有不同的数据结构。 -遍历器(Iterator)就是这样一种机制。它是一种接口,为各种不同的数据结构提供统一的访问机制。任何数据结构只要部署Iterator接口,就可以完成遍历操作(即依次处理该数据结构的所有成员)。 +遍历器(Iterator)就是这样一种机制。它是一种接口,为各种不同的数据结构提供统一的访问机制。任何数据结构只要部署 Iterator 接口,就可以完成遍历操作(即依次处理该数据结构的所有成员)。 -Iterator 的作用有三个:一是为各种数据结构,提供一个统一的、简便的访问接口;二是使得数据结构的成员能够按某种次序排列;三是ES6创造了一种新的遍历命令`for...of`循环,Iterator接口主要供`for...of`消费。 +Iterator 的作用有三个:一是为各种数据结构,提供一个统一的、简便的访问接口;二是使得数据结构的成员能够按某种次序排列;三是 ES6 创造了一种新的遍历命令`for...of`循环,Iterator 接口主要供`for...of`消费。 Iterator 的遍历过程是这样的。 @@ -363,7 +363,7 @@ let arr = ['b', 'c']; let arr = [...iterable]; ``` -**(3)yield* ** +**(3)yield\* ** `yield*`后面跟的是一个可遍历的结构,它会调用该结构的遍历器接口。 @@ -410,7 +410,7 @@ iterator.next() // { value: "i", done: false } iterator.next() // { value: undefined, done: true } ``` -上面代码中,调用`Symbol.iterator`方法返回一个遍历器对象,在这个遍历器上可以调用next方法,实现对于字符串的遍历。 +上面代码中,调用`Symbol.iterator`方法返回一个遍历器对象,在这个遍历器上可以调用 next 方法,实现对于字符串的遍历。 可以覆盖原生的`Symbol.iterator`方法,达到修改遍历器行为的目的。 @@ -437,11 +437,11 @@ str[Symbol.iterator] = function() { str // "hi" ``` -上面代码中,字符串str的`Symbol.iterator`方法被修改了,所以扩展运算符(`...`)返回的值变成了`bye`,而字符串本身还是`hi`。 +上面代码中,字符串 str 的`Symbol.iterator`方法被修改了,所以扩展运算符(`...`)返回的值变成了`bye`,而字符串本身还是`hi`。 -## Iterator接口与Generator函数 +## Iterator 接口与 Generator 函数 -`Symbol.iterator`方法的最简单实现,还是使用下一章要介绍的Generator函数。 +`Symbol.iterator`方法的最简单实现,还是使用下一章要介绍的 Generator 函数。 ```javascript var myIterable = {}; @@ -469,9 +469,9 @@ for (let x of obj) { // world ``` -上面代码中,`Symbol.iterator`方法几乎不用部署任何代码,只要用yield命令给出每一步的返回值即可。 +上面代码中,`Symbol.iterator`方法几乎不用部署任何代码,只要用 yield 命令给出每一步的返回值即可。 -## 遍历器对象的return(),throw() +## 遍历器对象的 return(),throw() 遍历器对象除了具有`next`方法,还可以具有`return`方法和`throw`方法。如果你自己写遍历器对象生成函数,那么`next`方法是必须部署的,`return`方法和`throw`方法是否部署是可选的。 @@ -517,13 +517,13 @@ for (let line of readLinesSync(fileName)) { 注意,`return`方法必须返回一个对象,这是 Generator 规格决定的。 -`throw`方法主要是配合 Generator 函数使用,一般的遍历器对象用不到这个方法。请参阅《Generator函数》一章。 +`throw`方法主要是配合 Generator 函数使用,一般的遍历器对象用不到这个方法。请参阅《Generator 函数》一章。 -## for...of循环 +## for...of 循环 ES6 借鉴 C++、Java、C# 和 Python 语言,引入了`for...of`循环,作为遍历所有数据结构的统一的方法。 -一个数据结构只要部署了`Symbol.iterator`属性,就被视为具有iterator接口,就可以用`for...of`循环遍历它的成员。也就是说,`for...of`循环内部调用的是数据结构的`Symbol.iterator`方法。 +一个数据结构只要部署了`Symbol.iterator`属性,就被视为具有 iterator 接口,就可以用`for...of`循环遍历它的成员。也就是说,`for...of`循环内部调用的是数据结构的`Symbol.iterator`方法。 `for...of`循环可以使用的范围包括数组、Set 和 Map 结构、某些类似数组的对象(比如`arguments`对象、DOM NodeList 对象)、后文的 Generator 对象,以及字符串。 @@ -636,7 +636,7 @@ for (let [key, value] of map) { ### 计算生成的数据结构 -有些数据结构是在现有数据结构的基础上,计算生成的。比如,ES6的数组、Set、Map 都部署了以下三个方法,调用后都返回遍历器对象。 +有些数据结构是在现有数据结构的基础上,计算生成的。比如,ES6 的数组、Set、Map 都部署了以下三个方法,调用后都返回遍历器对象。 - `entries()` 返回一个遍历器对象,用来遍历`[键名, 键值]`组成的数组。对于数组,键名就是索引值;对于 Set,键名与键值相同。Map 结构的 Iterator 接口,默认就是调用`entries`方法。 - `keys()` 返回一个遍历器对象,用来遍历所有的键名。 @@ -684,7 +684,7 @@ printArgs('a', 'b'); // 'b' ``` -对于字符串来说,`for...of`循环还有一个特点,就是会正确识别32位 UTF-16 字符。 +对于字符串来说,`for...of`循环还有一个特点,就是会正确识别 32 位 UTF-16 字符。 ```javascript for (let x of 'a\uD83D\uDC0A') { @@ -809,7 +809,7 @@ for (let value of myArray) { - 不同于`forEach`方法,它可以与`break`、`continue`和`return`配合使用。 - 提供了遍历所有数据结构的统一操作接口。 -下面是一个使用break语句,跳出`for...of`循环的例子。 +下面是一个使用 break 语句,跳出`for...of`循环的例子。 ```javascript for (var n of fibonacci) { @@ -819,5 +819,4 @@ for (var n of fibonacci) { } ``` -上面的例子,会输出斐波纳契数列小于等于1000的项。如果当前项大于1000,就会使用`break`语句跳出`for...of`循环。 - +上面的例子,会输出斐波纳契数列小于等于 1000 的项。如果当前项大于 1000,就会使用`break`语句跳出`for...of`循环。 diff --git a/docs/let.md b/docs/let.md index 735fbb771..a18adaa1b 100644 --- a/docs/let.md +++ b/docs/let.md @@ -43,9 +43,9 @@ for (var i = 0; i < 10; i++) { a[6](); // 10 ``` -上面代码中,变量`i`是`var`命令声明的,在全局范围内都有效,所以全局只有一个变量`i`。每一次循环,变量`i`的值都会发生改变,而循环内被赋给数组`a`的函数内部的`console.log(i)`,里面的`i`指向的就是全局的`i`。也就是说,所有数组`a`的成员里面的`i`,指向的都是同一个`i`,导致运行时输出的是最后一轮的`i`的值,也就是10。 +上面代码中,变量`i`是`var`命令声明的,在全局范围内都有效,所以全局只有一个变量`i`。每一次循环,变量`i`的值都会发生改变,而循环内被赋给数组`a`的函数内部的`console.log(i)`,里面的`i`指向的就是全局的`i`。也就是说,所有数组`a`的成员里面的`i`,指向的都是同一个`i`,导致运行时输出的是最后一轮的`i`的值,也就是 10。 -如果使用`let`,声明的变量仅在块级作用域内有效,最后输出的是6。 +如果使用`let`,声明的变量仅在块级作用域内有效,最后输出的是 6。 ```javascript var a = []; @@ -71,7 +71,7 @@ for (let i = 0; i < 3; i++) { // abc ``` -上面代码正确运行,输出了3次`abc`。这表明函数内部的变量`i`与循环变量`i`不在同一个作用域,有各自单独的作用域。 +上面代码正确运行,输出了 3 次`abc`。这表明函数内部的变量`i`与循环变量`i`不在同一个作用域,有各自单独的作用域。 ### 不存在变量提升 @@ -106,7 +106,7 @@ if (true) { 上面代码中,存在全局变量`tmp`,但是块级作用域内`let`又声明了一个局部变量`tmp`,导致后者绑定这个块级作用域,所以在`let`声明变量前,对`tmp`赋值会报错。 -ES6明确规定,如果区块中存在`let`和`const`命令,这个区块对这些命令声明的变量,从一开始就形成了封闭作用域。凡是在声明之前就使用这些变量,就会报错。 +ES6 明确规定,如果区块中存在`let`和`const`命令,这个区块对这些命令声明的变量,从一开始就形成了封闭作用域。凡是在声明之前就使用这些变量,就会报错。 总之,在代码块内,使用`let`命令声明变量之前,该变量都是不可用的。这在语法上,称为“暂时性死区”(temporal dead zone,简称 TDZ)。 @@ -262,7 +262,7 @@ function f1() { } ``` -上面的函数有两个代码块,都声明了变量`n`,运行后输出5。这表示外层代码块不受内层代码块的影响。如果两次都使用`var`定义变量`n`,最后输出的值才是10。 +上面的函数有两个代码块,都声明了变量`n`,运行后输出 5。这表示外层代码块不受内层代码块的影响。如果两次都使用`var`定义变量`n`,最后输出的值才是 10。 ES6 允许块级作用域的任意嵌套。 @@ -359,7 +359,7 @@ function f() { console.log('I am outside!'); } ES6 就完全不一样了,理论上会得到“I am outside!”。因为块级作用域内声明的函数类似于`let`,对作用域之外没有影响。但是,如果你真的在 ES6 浏览器中运行一下上面的代码,是会报错的,这是为什么呢? -原来,如果改变了块级作用域内声明的函数的处理规则,显然会对老代码产生很大影响。为了减轻因此产生的不兼容问题,ES6在[附录B](http://www.ecma-international.org/ecma-262/6.0/index.html#sec-block-level-function-declarations-web-legacy-compatibility-semantics)里面规定,浏览器的实现可以不遵守上面的规定,有自己的[行为方式](http://stackoverflow.com/questions/31419897/what-are-the-precise-semantics-of-block-level-functions-in-es6)。 +原来,如果改变了块级作用域内声明的函数的处理规则,显然会对老代码产生很大影响。为了减轻因此产生的不兼容问题,ES6 在[附录 B](http://www.ecma-international.org/ecma-262/6.0/index.html#sec-block-level-function-declarations-web-legacy-compatibility-semantics)里面规定,浏览器的实现可以不遵守上面的规定,有自己的[行为方式](http://stackoverflow.com/questions/31419897/what-are-the-precise-semantics-of-block-level-functions-in-es6)。 - 允许在块级作用域内声明函数。 - 函数声明类似于`var`,即会提升到全局作用域或函数作用域的头部。 @@ -571,7 +571,7 @@ var constantize = (obj) => { ### ES6 声明变量的六种方法 -ES5 只有两种声明变量的方法:`var`命令和`function`命令。ES6 除了添加`let`和`const`命令,后面章节还会提到,另外两种声明变量的方法:`import`命令和`class`命令。所以,ES6 一共有6种声明变量的方法。 +ES5 只有两种声明变量的方法:`var`命令和`function`命令。ES6 除了添加`let`和`const`命令,后面章节还会提到,另外两种声明变量的方法:`import`命令和`class`命令。所以,ES6 一共有 6 种声明变量的方法。 ## 顶层对象的属性 @@ -662,4 +662,3 @@ const global = getGlobal(); ``` 上面代码将顶层对象放入变量`global`。 - diff --git a/docs/mixin.md b/docs/mixin.md index 0c00d57c5..153bad301 100644 --- a/docs/mixin.md +++ b/docs/mixin.md @@ -1,12 +1,12 @@ # Mixin -JavaScript语言的设计是单一继承,即子类只能继承一个父类,不允许继承多个父类。这种设计保证了对象继承的层次结构是树状的,而不是复杂的[网状结构](https://en.wikipedia.org/wiki/Multiple_inheritance#The_diamond_problem)。 +JavaScript 语言的设计是单一继承,即子类只能继承一个父类,不允许继承多个父类。这种设计保证了对象继承的层次结构是树状的,而不是复杂的[网状结构](https://en.wikipedia.org/wiki/Multiple_inheritance#The_diamond_problem)。 但是,这大大降低了编程的灵活性。因为实际开发中,有时不可避免,子类需要继承多个父类。举例来说,“猫”可以继承“哺乳类动物”,也可以继承“宠物”。 -各种单一继承的编程语言,有不同的多重继承解决方案。比如,Java语言也是子类只能继承一个父类,但是还允许继承多个界面(interface),这样就间接实现了多重继承。Interface与父类一样,也是一个类,只不过它只定义接口(method signature),不定义实现,因此又被称为“抽象类”。凡是继承于Interface的方法,都必须自己定义实现,否则就会报错。这样就避免了多重继承的最大问题:多个父类的同名方法的碰撞(naming collision)。 +各种单一继承的编程语言,有不同的多重继承解决方案。比如,Java 语言也是子类只能继承一个父类,但是还允许继承多个界面(interface),这样就间接实现了多重继承。Interface 与父类一样,也是一个类,只不过它只定义接口(method signature),不定义实现,因此又被称为“抽象类”。凡是继承于 Interface 的方法,都必须自己定义实现,否则就会报错。这样就避免了多重继承的最大问题:多个父类的同名方法的碰撞(naming collision)。 -JavaScript语言没有采用Interface的方案,而是通过代理(delegation)实现了从其他类引入方法。 +JavaScript 语言没有采用 Interface 的方案,而是通过代理(delegation)实现了从其他类引入方法。 ```javascript var Enumerable_first = function () { @@ -24,15 +24,15 @@ list.first() // "foo" ## 含义 -Mixin这个名字来自于冰淇淋,在基本口味的冰淇淋上面混入其他口味,这就叫做Mix-in。 +Mixin 这个名字来自于冰淇淋,在基本口味的冰淇淋上面混入其他口味,这就叫做 Mix-in。 它允许向一个类里面注入一些代码,使得一个类的功能能够“混入”另一个类。实质上是多重继承的一种解决方案,但是避免了多重继承的复杂性,而且有利于代码复用。 -Mixin就是一个正常的类,不仅定义了接口,还定义了接口的实现。 +Mixin 就是一个正常的类,不仅定义了接口,还定义了接口的实现。 子类通过在`this`对象上面绑定方法,达到多重继承的目的。 -很多库提供了Mixin功能。下面以Lodash为例。 +很多库提供了 Mixin 功能。下面以 Lodash 为例。 ```javascript function vowels(string) { @@ -44,9 +44,9 @@ _.mixin(obj, {vowels: vowels}) obj.vowels() // true ``` -上面代码通过Lodash库的`_.mixin`方法,让`obj`对象继承了`vowels`方法。 +上面代码通过 Lodash 库的`_.mixin`方法,让`obj`对象继承了`vowels`方法。 -Underscore的类似方法是`_.extend`。 +Underscore 的类似方法是`_.extend`。 ```javascript var Person = function (fName, lName) { @@ -90,7 +90,7 @@ function extend(destination, source) { ## Trait -Trait是另外一种多重继承的解决方案。它与Mixin很相似,但是有一些细微的差别。 +Trait 是另外一种多重继承的解决方案。它与 Mixin 很相似,但是有一些细微的差别。 -- Mixin可以包含状态(state),Trait不包含,即Trait里面的方法都是互不相干,可以线性包含的。比如,`Trait1`包含方法`A`和`B`,`Trait2`继承了`Trait1`,同时还包含一个自己的方法`C`,实际上就等同于直接包含方法`A`、`B`、`C`。 -- 对于同名方法的碰撞,Mixin包含了解决规则,Trait则是报错。 +- Mixin 可以包含状态(state),Trait 不包含,即 Trait 里面的方法都是互不相干,可以线性包含的。比如,`Trait1`包含方法`A`和`B`,`Trait2`继承了`Trait1`,同时还包含一个自己的方法`C`,实际上就等同于直接包含方法`A`、`B`、`C`。 +- 对于同名方法的碰撞,Mixin 包含了解决规则,Trait 则是报错。 diff --git a/docs/module-loader.md b/docs/module-loader.md index 2dbd84829..da04d39c8 100644 --- a/docs/module-loader.md +++ b/docs/module-loader.md @@ -194,7 +194,7 @@ console.log(foo); setTimeout(() => console.log(foo), 500); ``` -上面代码中,`m1.js`的变量`foo`,在刚加载时等于`bar`,过了500毫秒,又变为等于`baz`。 +上面代码中,`m1.js`的变量`foo`,在刚加载时等于`bar`,过了 500 毫秒,又变为等于`baz`。 让我们看看,`m2.js`能否正确读取这个变化。 @@ -694,7 +694,7 @@ export function odd(n) { } ``` -上面代码中,`even.js`里面的函数`even`有一个参数`n`,只要不等于0,就会减去1,传入加载的`odd()`。`odd.js`也会做类似操作。 +上面代码中,`even.js`里面的函数`even`有一个参数`n`,只要不等于 0,就会减去 1,传入加载的`odd()`。`odd.js`也会做类似操作。 运行上面这段代码,结果如下。 @@ -711,7 +711,7 @@ true 17 ``` -上面代码中,参数`n`从10变为0的过程中,`even()`一共会执行6次,所以变量`counter`等于6。第二次调用`even()`时,参数`n`从20变为0,`even()`一共会执行11次,加上前面的6次,所以变量`counter`等于17。 +上面代码中,参数`n`从 10 变为 0 的过程中,`even()`一共会执行 6 次,所以变量`counter`等于 6。第二次调用`even()`时,参数`n`从 20 变为 0,`even()`一共会执行 11 次,加上前面的 6 次,所以变量`counter`等于 17。 这个例子要是改写成 CommonJS,就根本无法执行,会报错。 @@ -785,7 +785,7 @@ $ compile-modules convert -o out.js file1.js ``` -上面代码中的`./app`,指的是当前目录下的app.js文件。它可以是ES6模块文件,`System.import`会自动将其转码。 +上面代码中的`./app`,指的是当前目录下的 app.js 文件。它可以是 ES6 模块文件,`System.import`会自动将其转码。 需要注意的是,`System.import`使用异步加载,返回一个 Promise 对象,可以针对这个对象编程。下面是一个模块文件。 @@ -812,4 +812,3 @@ System.import('app/es6-file').then(function(m) { ``` 上面代码中,`System.import`方法返回的是一个 Promise 对象,所以可以用`then`方法指定回调函数。 - diff --git a/docs/module.md b/docs/module.md index ca041ca96..4c9add1c1 100644 --- a/docs/module.md +++ b/docs/module.md @@ -19,7 +19,7 @@ let exists = _fs.exists; let readfile = _fs.readfile; ``` -上面代码的实质是整体加载`fs`模块(即加载`fs`的所有方法),生成一个对象(`_fs`),然后再从这个对象上面读取3个方法。这种加载称为“运行时加载”,因为只有运行时才能得到这个对象,导致完全没办法在编译时做“静态优化”。 +上面代码的实质是整体加载`fs`模块(即加载`fs`的所有方法),生成一个对象(`_fs`),然后再从这个对象上面读取 3 个方法。这种加载称为“运行时加载”,因为只有运行时才能得到这个对象,导致完全没办法在编译时做“静态优化”。 ES6 模块不是对象,而是通过`export`命令显式指定输出的代码,再通过`import`命令输入。 @@ -28,7 +28,7 @@ ES6 模块不是对象,而是通过`export`命令显式指定输出的代码 import { stat, exists, readFile } from 'fs'; ``` -上面代码的实质是从`fs`模块加载3个方法,其他方法不加载。这种加载称为“编译时加载”或者静态加载,即 ES6 可以在编译时就完成模块加载,效率要比 CommonJS 模块的加载方式高。当然,这也导致了没法引用 ES6 模块本身,因为它不是对象。 +上面代码的实质是从`fs`模块加载 3 个方法,其他方法不加载。这种加载称为“编译时加载”或者静态加载,即 ES6 可以在编译时就完成模块加载,效率要比 CommonJS 模块的加载方式高。当然,这也导致了没法引用 ES6 模块本身,因为它不是对象。 由于 ES6 模块是编译时加载,使得静态分析成为可能。有了它,就能进一步拓宽 JavaScript 的语法,比如引入宏(macro)和类型检验(type system)这些只能靠静态分析实现的功能。 @@ -50,7 +50,7 @@ ES6 的模块自动采用严格模式,不管你有没有在模块头部加上` - 函数的参数不能有同名属性,否则报错 - 不能使用`with`语句 - 不能对只读属性赋值,否则报错 -- 不能使用前缀0表示八进制数,否则报错 +- 不能使用前缀 0 表示八进制数,否则报错 - 不能删除不可删除的属性,否则报错 - 不能删除变量`delete prop`,会报错,只能删除属性`delete global[prop]` - `eval`不会在它的外层作用域引入变量 @@ -130,7 +130,7 @@ var m = 1; export m; ``` -上面两种写法都会报错,因为没有提供对外的接口。第一种写法直接输出1,第二种写法通过变量`m`,还是直接输出1。`1`只是一个值,不是接口。正确的写法是下面这样。 +上面两种写法都会报错,因为没有提供对外的接口。第一种写法直接输出 1,第二种写法通过变量`m`,还是直接输出 1。`1`只是一个值,不是接口。正确的写法是下面这样。 ```javascript // 写法一 @@ -169,11 +169,11 @@ export var foo = 'bar'; setTimeout(() => foo = 'baz', 500); ``` -上面代码输出变量`foo`,值为`bar`,500毫秒之后变成`baz`。 +上面代码输出变量`foo`,值为`bar`,500 毫秒之后变成`baz`。 这一点与 CommonJS 规范完全不同。CommonJS 模块输出的是值的缓存,不存在动态更新,详见下文《Module 的加载实现》一节。 -最后,`export`命令可以出现在模块的任何位置,只要处于模块顶层就可以。如果处于块级作用域内,就会报错,下一节的`import`命令也是如此。这是因为处于条件代码块之中,就没法做静态优化了,违背了ES6模块的设计初衷。 +最后,`export`命令可以出现在模块的任何位置,只要处于模块顶层就可以。如果处于块级作用域内,就会报错,下一节的`import`命令也是如此。这是因为处于条件代码块之中,就没法做静态优化了,违背了 ES6 模块的设计初衷。 ```javascript function foo() { @@ -784,4 +784,3 @@ async function main() { } main(); ``` - diff --git a/docs/number.md b/docs/number.md index 680267fc0..1311318e0 100644 --- a/docs/number.md +++ b/docs/number.md @@ -132,7 +132,7 @@ Number.parseFloat === parseFloat // true ## Number.isInteger() -`Number.isInteger()`用来判断一个值是否为整数。需要注意的是,在 JavaScript 内部,整数和浮点数是同样的储存方法,所以3和3.0被视为同一个值。 +`Number.isInteger()`用来判断一个值是否为整数。需要注意的是,在 JavaScript 内部,整数和浮点数是同样的储存方法,所以 3 和 3.0 被视为同一个值。 ```javascript Number.isInteger(25) // true @@ -164,9 +164,9 @@ ES5 可以通过下面的代码,部署`Number.isInteger()`。 ## Number.EPSILON -ES6 在`Number`对象上面,新增一个极小的常量`Number.EPSILON`。根据规格,它表示1与大于1的最小浮点数之间的差。 +ES6 在`Number`对象上面,新增一个极小的常量`Number.EPSILON`。根据规格,它表示 1 与大于 1 的最小浮点数之间的差。 -对于64位浮点数来说,大于1的最小浮点数相当于二进制的`1.00..001`,小数点后面有连续51个零。这个值减去1之后,就等于2的-52次方。 +对于 64 位浮点数来说,大于 1 的最小浮点数相当于二进制的`1.00..001`,小数点后面有连续 51 个零。这个值减去 1 之后,就等于 2 的-52 次方。 ```javascript Number.EPSILON === Math.pow(2, -52) @@ -198,7 +198,7 @@ Number.EPSILON.toFixed(20) 0.1 + 0.2 === 0.3 // false ``` -`Number.EPSILON`可以用来设置“能够接受的误差范围”。比如,误差范围设为2的-50次方(即`Number.EPSILON * Math.pow(2, 2)`),即如果两个浮点数的差小于这个值,我们就认为这两个浮点数相等。 +`Number.EPSILON`可以用来设置“能够接受的误差范围”。比如,误差范围设为 2 的-50 次方(即`Number.EPSILON * Math.pow(2, 2)`),即如果两个浮点数的差小于这个值,我们就认为这两个浮点数相等。 ```javascript 5.551115123125783e-17 < Number.EPSILON * Math.pow(2, 2) @@ -235,9 +235,9 @@ Math.pow(2, 53) === Math.pow(2, 53) + 1 // true ``` -上面代码中,超出2的53次方之后,一个数就不精确了。 +上面代码中,超出 2 的 53 次方之后,一个数就不精确了。 -ES6引入了`Number.MAX_SAFE_INTEGER`和`Number.MIN_SAFE_INTEGER`这两个常量,用来表示这个范围的上下限。 +ES6 引入了`Number.MAX_SAFE_INTEGER`和`Number.MIN_SAFE_INTEGER`这两个常量,用来表示这个范围的上下限。 ```javascript Number.MAX_SAFE_INTEGER === Math.pow(2, 53) - 1 @@ -251,7 +251,7 @@ Number.MIN_SAFE_INTEGER === -9007199254740991 // true ``` -上面代码中,可以看到JavaScript能够精确表示的极限。 +上面代码中,可以看到 JavaScript 能够精确表示的极限。 `Number.isSafeInteger()`则是用来判断一个整数是否落在这个范围之内。 @@ -326,9 +326,9 @@ trusty(1, 2, 3) // 3 ``` -## Math对象的扩展 +## Math 对象的扩展 -ES6在Math对象上新增了17个与数学相关的方法。所有这些方法都是静态方法,只能在Math对象上调用。 +ES6 在 Math 对象上新增了 17 个与数学相关的方法。所有这些方法都是静态方法,只能在 Math 对象上调用。 ### Math.trunc() @@ -376,7 +376,7 @@ Math.trunc = Math.trunc || function(x) { - 参数为正数,返回`+1`; - 参数为负数,返回`-1`; -- 参数为0,返回`0`; +- 参数为 0,返回`0`; - 参数为-0,返回`-0`; - 其他值,返回`NaN`。 @@ -442,7 +442,7 @@ Math.cbrt = Math.cbrt || function(x) { ### Math.clz32() -JavaScript的整数使用32位二进制形式表示,`Math.clz32`方法返回一个数的32位无符号整数形式有多少个前导0。 +JavaScript 的整数使用 32 位二进制形式表示,`Math.clz32`方法返回一个数的 32 位无符号整数形式有多少个前导 0。 ```javascript Math.clz32(0) // 32 @@ -452,9 +452,9 @@ Math.clz32(0b01000000000000000000000000000000) // 1 Math.clz32(0b00100000000000000000000000000000) // 2 ``` -上面代码中,0的二进制形式全为0,所以有32个前导0;1的二进制形式是`0b1`,只占1位,所以32位之中有31个前导0;1000的二进制形式是`0b1111101000`,一共有10位,所以32位之中有22个前导0。 +上面代码中,0 的二进制形式全为 0,所以有 32 个前导 0;1 的二进制形式是`0b1`,只占 1 位,所以 32 位之中有 31 个前导 0;1000 的二进制形式是`0b1111101000`,一共有 10 位,所以 32 位之中有 22 个前导 0。 -`clz32`这个函数名就来自”count leading zero bits in 32-bit binary representation of a number“(计算一个数的32位二进制形式的前导0的个数)的缩写。 +`clz32`这个函数名就来自”count leading zero bits in 32-bit binary representation of a number“(计算一个数的 32 位二进制形式的前导 0 的个数)的缩写。 左移运算符(`<<`)与`Math.clz32`方法直接相关。 @@ -488,7 +488,7 @@ Math.clz32(true) // 31 ### Math.imul() -`Math.imul`方法返回两个数以32位带符号整数形式相乘的结果,返回的也是一个32位的带符号整数。 +`Math.imul`方法返回两个数以 32 位带符号整数形式相乘的结果,返回的也是一个 32 位的带符号整数。 ```javascript Math.imul(2, 4) // 8 @@ -496,13 +496,13 @@ Math.imul(-1, 8) // -8 Math.imul(-2, -2) // 4 ``` -如果只考虑最后32位,大多数情况下,`Math.imul(a, b)`与`a * b`的结果是相同的,即该方法等同于`(a * b)|0`的效果(超过32位的部分溢出)。之所以需要部署这个方法,是因为JavaScript有精度限制,超过2的53次方的值无法精确表示。这就是说,对于那些很大的数的乘法,低位数值往往都是不精确的,`Math.imul`方法可以返回正确的低位数值。 +如果只考虑最后 32 位,大多数情况下,`Math.imul(a, b)`与`a * b`的结果是相同的,即该方法等同于`(a * b)|0`的效果(超过 32 位的部分溢出)。之所以需要部署这个方法,是因为 JavaScript 有精度限制,超过 2 的 53 次方的值无法精确表示。这就是说,对于那些很大的数的乘法,低位数值往往都是不精确的,`Math.imul`方法可以返回正确的低位数值。 ```javascript (0x7fffffff * 0x7fffffff)|0 // 0 ``` -上面这个乘法算式,返回结果为0。但是由于这两个二进制数的最低位都是1,所以这个结果肯定是不正确的,因为根据二进制乘法,计算结果的二进制最低位应该也是1。这个错误就是因为它们的乘积超过了2的53次方,JavaScript无法保存额外的精度,就把低位的值都变成了0。`Math.imul`方法可以返回正确的值1。 +上面这个乘法算式,返回结果为 0。但是由于这两个二进制数的最低位都是 1,所以这个结果肯定是不正确的,因为根据二进制乘法,计算结果的二进制最低位应该也是 1。这个错误就是因为它们的乘积超过了 2 的 53 次方,JavaScript 无法保存额外的精度,就把低位的值都变成了 0。`Math.imul`方法可以返回正确的值 1。 ```javascript Math.imul(0x7fffffff, 0x7fffffff) // 1 @@ -510,7 +510,7 @@ Math.imul(0x7fffffff, 0x7fffffff) // 1 ### Math.fround() -Math.fround方法返回一个数的单精度浮点数形式。 +Math.fround 方法返回一个数的单精度浮点数形式。 ```javascript Math.fround(0) // 0 @@ -520,7 +520,7 @@ Math.fround(1.5) // 1.5 Math.fround(NaN) // NaN ``` -对于整数来说,`Math.fround`方法返回结果不会有任何不同,区别主要是那些无法用64个二进制位精确表示的小数。这时,`Math.fround`方法会返回最接近这个小数的单精度浮点数。 +对于整数来说,`Math.fround`方法返回结果不会有任何不同,区别主要是那些无法用 64 个二进制位精确表示的小数。这时,`Math.fround`方法会返回最接近这个小数的单精度浮点数。 对于没有部署这个方法的环境,可以用下面的代码模拟。 @@ -544,17 +544,17 @@ Math.hypot(3, 4, '5'); // 7.0710678118654755 Math.hypot(-3); // 3 ``` -上面代码中,3的平方加上4的平方,等于5的平方。 +上面代码中,3 的平方加上 4 的平方,等于 5 的平方。 -如果参数不是数值,`Math.hypot`方法会将其转为数值。只要有一个参数无法转为数值,就会返回NaN。 +如果参数不是数值,`Math.hypot`方法会将其转为数值。只要有一个参数无法转为数值,就会返回 NaN。 ### 对数方法 -ES6新增了4个对数相关方法。 +ES6 新增了 4 个对数相关方法。 **(1) Math.expm1()** -`Math.expm1(x)`返回ex - 1,即`Math.exp(x) - 1`。 +`Math.expm1(x)`返回 ex - 1,即`Math.exp(x) - 1`。 ```javascript Math.expm1(-1) // -0.6321205588285577 @@ -591,7 +591,7 @@ Math.log1p = Math.log1p || function(x) { **(3)Math.log10()** -`Math.log10(x)`返回以10为底的`x`的对数。如果`x`小于0,则返回NaN。 +`Math.log10(x)`返回以 10 为底的`x`的对数。如果`x`小于 0,则返回 NaN。 ```javascript Math.log10(2) // 0.3010299956639812 @@ -611,7 +611,7 @@ Math.log10 = Math.log10 || function(x) { **(4)Math.log2()** -`Math.log2(x)`返回以2为底的`x`的对数。如果`x`小于0,则返回NaN。 +`Math.log2(x)`返回以 2 为底的`x`的对数。如果`x`小于 0,则返回 NaN。 ```javascript Math.log2(3) // 1.584962500721156 @@ -633,7 +633,7 @@ Math.log2 = Math.log2 || function(x) { ### 双曲函数方法 -ES6新增了6个双曲函数方法。 +ES6 新增了 6 个双曲函数方法。 - `Math.sinh(x)` 返回`x`的双曲正弦(hyperbolic sine) - `Math.cosh(x)` 返回`x`的双曲余弦(hyperbolic cosine) @@ -650,7 +650,7 @@ ES6新增了6个双曲函数方法。 Math.sign(-0) // -0 ``` -这导致对于判断符号位的正负,`Math.sign()`不是很有用。JavaScript 内部使用64位浮点数(国际标准IEEE 754)表示数值,IEEE 754规定第一位是符号位,`0`表示正数,`1`表示负数。所以会有两种零,`+0`是符号位为`0`时的零值,`-0`是符号位为`1`时的零值。实际编程中,判断一个值是`+0`还是`-0`非常麻烦,因为它们是相等的。 +这导致对于判断符号位的正负,`Math.sign()`不是很有用。JavaScript 内部使用 64 位浮点数(国际标准 IEEE 754)表示数值,IEEE 754 规定第一位是符号位,`0`表示正数,`1`表示负数。所以会有两种零,`+0`是符号位为`0`时的零值,`-0`是符号位为`1`时的零值。实际编程中,判断一个值是`+0`还是`-0`非常麻烦,因为它们是相等的。 ```javascript +0 === -0 // true @@ -711,7 +711,7 @@ Math.pow(99, 99) ### 简介 -JavaScript 所有数字都保存成64位浮点数,这决定了整数的精确程度只能到53个二进制位。大于这个范围的整数,JavaScript 是无法精确表示的,这使得 JavaScript 不适合进行科学和金融方面的精确计算。 +JavaScript 所有数字都保存成 64 位浮点数,这决定了整数的精确程度只能到 53 个二进制位。大于这个范围的整数,JavaScript 是无法精确表示的,这使得 JavaScript 不适合进行科学和金融方面的精确计算。 现在有一个[提案](https://github.com/tc39/proposal-bigint),引入了新的数据类型 Integer(整数),来解决这个问题。整数类型的数据只用来表示整数,没有位数的限制,任何位数的整数都可以精确表示。 @@ -764,7 +764,7 @@ Integer('abc') // SyntaxError // 1n ``` -几乎所有的 Number 运算符都可以用在 Integer,但是有两个除外:不带符号的右移位运算符`>>>`和一元的求正运算符`+`,使用时会报错。前者是因为`>>>`要求最高位补0,但是 Integer 类型没有最高位,导致这个运算符无意义。后者是因为一元运算符`+`在 asm.js 里面总是返回 Number 类型或者报错。 +几乎所有的 Number 运算符都可以用在 Integer,但是有两个除外:不带符号的右移位运算符`>>>`和一元的求正运算符`+`,使用时会报错。前者是因为`>>>`要求最高位补 0,但是 Integer 类型没有最高位,导致这个运算符无意义。后者是因为一元运算符`+`在 asm.js 里面总是返回 Number 类型或者报错。 Integer 类型不能与 Number 类型进行混合运算。 @@ -791,4 +791,3 @@ Integer 类型不能与 Number 类型进行混合运算。 0n === 0 // false ``` - diff --git a/docs/object.md b/docs/object.md index 0dee9af8f..bec0117f5 100644 --- a/docs/object.md +++ b/docs/object.md @@ -479,7 +479,7 @@ Object.assign([1, 2, 3], [4, 5]) // [4, 5, 3] ``` -上面代码中,`Object.assign`把数组视为属性名为0、1、2的对象,因此源数组的0号属性`4`覆盖了目标数组的0号属性`1`。 +上面代码中,`Object.assign`把数组视为属性名为 0、1、2 的对象,因此源数组的 0 号属性`4`覆盖了目标数组的 0 号属性`1`。 **(4)取值函数的处理** @@ -604,7 +604,7 @@ processContent({ url: {port: 8000} }) // } ``` -上面代码的原意是将`url.port`改成8000,`url.host`不变。实际结果却是`options.url`覆盖掉`DEFAULTS.url`,所以`url.host`就不存在了。 +上面代码的原意是将`url.port`改成 8000,`url.host`不变。实际结果却是`options.url`覆盖掉`DEFAULTS.url`,所以`url.host`就不存在了。 ## 属性的可枚举性和遍历 @@ -655,7 +655,7 @@ Object.getOwnPropertyDescriptor(class {foo() {}}.prototype, 'foo').enumerable ### 属性的遍历 -ES6 一共有5种方法可以遍历对象的属性。 +ES6 一共有 5 种方法可以遍历对象的属性。 **(1)for...in** @@ -677,7 +677,7 @@ ES6 一共有5种方法可以遍历对象的属性。 `Reflect.ownKeys`返回一个数组,包含对象自身的所有键名,不管键名是 Symbol 或字符串,也不管是否可枚举。 -以上的5种方法遍历对象的键名,都遵守同样的属性遍历的次序规则。 +以上的 5 种方法遍历对象的键名,都遵守同样的属性遍历的次序规则。 - 首先遍历所有数值键,按照数值升序排列。 - 其次遍历所有字符串键,按照加入时间升序排列。 @@ -1489,10 +1489,10 @@ const firstName = message?.body?.user?.firstName || 'default'; “Null 传导运算符”有四种用法。 -- `obj?.prop` // 读取对象属性 -- `obj?.[expr]` // 同上 +- `obj?.prop` // 读取对象属性 +- `obj?.[expr]` // 同上 - `func?.(...args)` // 函数或对象方法的调用 -- `new C?.(...args)` // 构造函数的调用 +- `new C?.(...args)` // 构造函数的调用 传导运算符之所以写成`obj?.prop`,而不是`obj?prop`,是为了方便编译器能够区分三元运算符`?:`(比如`obj?prop:123`)。 @@ -1510,4 +1510,3 @@ a?.b = 42 // 如果 a 是 null 或 undefined,下面的语句不产生任何效果 delete a?.b ``` - diff --git a/docs/promise.md b/docs/promise.md index 8b8c0fc00..c87f0552b 100644 --- a/docs/promise.md +++ b/docs/promise.md @@ -181,7 +181,7 @@ p2 // Error: fail ``` -上面代码中,`p1`是一个Promise,3秒之后变为`rejected`。`p2`的状态在1秒之后改变,`resolve`方法返回的是`p1`。由于`p2`返回的是另一个 Promise,导致`p2`自己的状态无效了,由`p1`的状态决定`p2`的状态。所以,后面的`then`语句都变成针对后者(`p1`)。又过了2秒,`p1`变为`rejected`,导致触发`catch`方法指定的回调函数。 +上面代码中,`p1`是一个 Promise,3 秒之后变为`rejected`。`p2`的状态在 1 秒之后改变,`resolve`方法返回的是`p1`。由于`p2`返回的是另一个 Promise,导致`p2`自己的状态无效了,由`p1`的状态决定`p2`的状态。所以,后面的`then`语句都变成针对后者(`p1`)。又过了 2 秒,`p1`变为`rejected`,导致触发`catch`方法指定的回调函数。 注意,调用`resolve`或`reject`并不会终结 Promise 的参数函数的执行。 @@ -311,7 +311,7 @@ promise.catch(function(error) { 比较上面两种写法,可以发现`reject`方法的作用,等同于抛出错误。 -如果Promise状态已经变成`resolved`,再抛出错误是无效的。 +如果 Promise 状态已经变成`resolved`,再抛出错误是无效的。 ```javascript const promise = new Promise(function(resolve, reject) { @@ -338,9 +338,9 @@ getJSON('/post/1.json').then(function(post) { }); ``` -上面代码中,一共有三个Promise对象:一个由`getJSON`产生,两个由`then`产生。它们之中任何一个抛出的错误,都会被最后一个`catch`捕获。 +上面代码中,一共有三个 Promise 对象:一个由`getJSON`产生,两个由`then`产生。它们之中任何一个抛出的错误,都会被最后一个`catch`捕获。 -一般来说,不要在`then`方法里面定义Reject状态的回调函数(即`then`的第二个参数),总是使用`catch`方法。 +一般来说,不要在`then`方法里面定义 Reject 状态的回调函数(即`then`的第二个参数),总是使用`catch`方法。 ```javascript // bad @@ -382,7 +382,7 @@ setTimeout(() => { console.log(123) }, 2000); // 123 ``` -上面代码中,`someAsyncThing`函数产生的 Promise 对象,内部有语法错误。浏览器运行到这一行,会打印出错误提示`ReferenceError: x is not defined`,但是不会退出进程、终止脚本执行,2秒之后还是会输出`123`。这就是说,Promise 内部的错误不会影响到 Promise 外部的代码,通俗的说法就是“Promise 会吃掉错误”。 +上面代码中,`someAsyncThing`函数产生的 Promise 对象,内部有语法错误。浏览器运行到这一行,会打印出错误提示`ReferenceError: x is not defined`,但是不会退出进程、终止脚本执行,2 秒之后还是会输出`123`。这就是说,Promise 内部的错误不会影响到 Promise 外部的代码,通俗的说法就是“Promise 会吃掉错误”。 这个脚本放在服务器执行,退出码就是`0`(即表示执行成功)。不过,Node 有一个`unhandledRejection`事件,专门监听未捕获的`reject`错误,上面的脚本会触发这个事件的监听函数,可以在监听函数里面抛出错误。 @@ -394,7 +394,7 @@ process.on('unhandledRejection', function (err, p) { 上面代码中,`unhandledRejection`事件的监听函数有两个参数,第一个是错误对象,第二个是报错的 Promise 实例,它可以用来了解发生错误的环境信息。 -注意,Node 有计划在未来废除`unhandledRejection`事件。如果 Promise 内部有未捕获的错误,会直接终止进程,并且进程的退出码不为0。 +注意,Node 有计划在未来废除`unhandledRejection`事件。如果 Promise 内部有未捕获的错误,会直接终止进程,并且进程的退出码不为 0。 再看下面的例子。 @@ -517,7 +517,7 @@ Promise.all(promises).then(function (posts) { }); ``` -上面代码中,`promises`是包含6个 Promise 实例的数组,只有这6个实例的状态都变成`fulfilled`,或者其中有一个变为`rejected`,才会调用`Promise.all`方法后面的回调函数。 +上面代码中,`promises`是包含 6 个 Promise 实例的数组,只有这 6 个实例的状态都变成`fulfilled`,或者其中有一个变为`rejected`,才会调用`Promise.all`方法后面的回调函数。 下面是另一个例子。 @@ -583,7 +583,7 @@ Promise.all([p1, p2]) ## Promise.race() -`Promise.race`方法同样是将多个Promise实例,包装成一个新的Promise实例。 +`Promise.race`方法同样是将多个 Promise 实例,包装成一个新的 Promise 实例。 ```javascript const p = Promise.race([p1, p2, p3]); @@ -593,7 +593,7 @@ const p = Promise.race([p1, p2, p3]); `Promise.race`方法的参数与`Promise.all`方法一样,如果不是 Promise 实例,就会先调用下面讲到的`Promise.resolve`方法,将参数转为 Promise 实例,再进一步处理。 -下面是一个例子,如果指定时间内没有获得结果,就将Promise的状态变为`reject`,否则变为`resolve`。 +下面是一个例子,如果指定时间内没有获得结果,就将 Promise 的状态变为`reject`,否则变为`resolve`。 ```javascript const p = Promise.race([ @@ -606,17 +606,17 @@ p.then(response => console.log(response)); p.catch(error => console.log(error)); ``` -上面代码中,如果5秒之内`fetch`方法无法返回结果,变量`p`的状态就会变为`rejected`,从而触发`catch`方法指定的回调函数。 +上面代码中,如果 5 秒之内`fetch`方法无法返回结果,变量`p`的状态就会变为`rejected`,从而触发`catch`方法指定的回调函数。 ## Promise.resolve() -有时需要将现有对象转为Promise对象,`Promise.resolve`方法就起到这个作用。 +有时需要将现有对象转为 Promise 对象,`Promise.resolve`方法就起到这个作用。 ```javascript const jsPromise = Promise.resolve($.ajax('/whatever.json')); ``` -上面代码将jQuery生成的`deferred`对象,转为一个新的Promise对象。 +上面代码将 jQuery 生成的`deferred`对象,转为一个新的 Promise 对象。 `Promise.resolve`等价于下面的写法。 @@ -628,9 +628,9 @@ new Promise(resolve => resolve('foo')) `Promise.resolve`方法的参数分成四种情况。 -**(1)参数是一个Promise实例** +**(1)参数是一个 Promise 实例** -如果参数是Promise实例,那么`Promise.resolve`将不做任何修改、原封不动地返回这个实例。 +如果参数是 Promise 实例,那么`Promise.resolve`将不做任何修改、原封不动地返回这个实例。 **(2)参数是一个`thenable`对象** @@ -644,7 +644,7 @@ let thenable = { }; ``` -`Promise.resolve`方法会将这个对象转为Promise对象,然后就立即执行`thenable`对象的`then`方法。 +`Promise.resolve`方法会将这个对象转为 Promise 对象,然后就立即执行`thenable`对象的`then`方法。 ```javascript let thenable = { @@ -659,11 +659,11 @@ p1.then(function(value) { }); ``` -上面代码中,`thenable`对象的`then`方法执行后,对象`p1`的状态就变为`resolved`,从而立即执行最后那个`then`方法指定的回调函数,输出42。 +上面代码中,`thenable`对象的`then`方法执行后,对象`p1`的状态就变为`resolved`,从而立即执行最后那个`then`方法指定的回调函数,输出 42。 **(3)参数不是具有`then`方法的对象,或根本就不是对象** -如果参数是一个原始值,或者是一个不具有`then`方法的对象,则`Promise.resolve`方法返回一个新的Promise对象,状态为`resolved`。 +如果参数是一个原始值,或者是一个不具有`then`方法的对象,则`Promise.resolve`方法返回一个新的 Promise 对象,状态为`resolved`。 ```javascript const p = Promise.resolve('Hello'); @@ -674,13 +674,13 @@ p.then(function (s){ // Hello ``` -上面代码生成一个新的Promise对象的实例`p`。由于字符串`Hello`不属于异步操作(判断方法是字符串对象不具有then方法),返回Promise实例的状态从一生成就是`resolved`,所以回调函数会立即执行。`Promise.resolve`方法的参数,会同时传给回调函数。 +上面代码生成一个新的 Promise 对象的实例`p`。由于字符串`Hello`不属于异步操作(判断方法是字符串对象不具有 then 方法),返回 Promise 实例的状态从一生成就是`resolved`,所以回调函数会立即执行。`Promise.resolve`方法的参数,会同时传给回调函数。 **(4)不带有任何参数** -`Promise.resolve`方法允许调用时不带参数,直接返回一个`resolved`状态的Promise对象。 +`Promise.resolve`方法允许调用时不带参数,直接返回一个`resolved`状态的 Promise 对象。 -所以,如果希望得到一个Promise对象,比较方便的方法就是直接调用`Promise.resolve`方法。 +所以,如果希望得到一个 Promise 对象,比较方便的方法就是直接调用`Promise.resolve`方法。 ```javascript const p = Promise.resolve(); @@ -690,9 +690,9 @@ p.then(function () { }); ``` -上面代码的变量`p`就是一个Promise对象。 +上面代码的变量`p`就是一个 Promise 对象。 -需要注意的是,立即`resolve`的Promise对象,是在本轮“事件循环”(event loop)的结束时,而不是在下一轮“事件循环”的开始时。 +需要注意的是,立即`resolve`的 Promise 对象,是在本轮“事件循环”(event loop)的结束时,而不是在下一轮“事件循环”的开始时。 ```javascript setTimeout(function () { @@ -727,7 +727,7 @@ p.then(null, function (s) { // 出错了 ``` -上面代码生成一个Promise对象的实例`p`,状态为`rejected`,回调函数会立即执行。 +上面代码生成一个 Promise 对象的实例`p`,状态为`rejected`,回调函数会立即执行。 注意,`Promise.reject()`方法的参数,会原封不动地作为`reject`的理由,变成后续方法的参数。这一点与`Promise.resolve`方法不一致。 @@ -749,11 +749,11 @@ Promise.reject(thenable) ## 两个有用的附加方法 -ES6的Promise API提供的方法不是很多,有些有用的方法可以自己部署。下面介绍如何部署两个不在ES6之中、但很有用的方法。 +ES6 的 Promise API 提供的方法不是很多,有些有用的方法可以自己部署。下面介绍如何部署两个不在 ES6 之中、但很有用的方法。 ### done() -Promise对象的回调链,不管以`then`方法或`catch`方法结尾,要是最后一个方法抛出错误,都有可能无法捕捉到(因为Promise内部的错误不会冒泡到全局)。因此,我们可以提供一个`done`方法,总是处于回调链的尾端,保证抛出任何可能出现的错误。 +Promise 对象的回调链,不管以`then`方法或`catch`方法结尾,要是最后一个方法抛出错误,都有可能无法捕捉到(因为 Promise 内部的错误不会冒泡到全局)。因此,我们可以提供一个`done`方法,总是处于回调链的尾端,保证抛出任何可能出现的错误。 ```javascript asyncFunc() @@ -779,9 +779,9 @@ Promise.prototype.done = function (onFulfilled, onRejected) { ### finally() -`finally`方法用于指定不管Promise对象最后状态如何,都会执行的操作。它与`done`方法的最大区别,它接受一个普通的回调函数作为参数,该函数不管怎样都必须执行。 +`finally`方法用于指定不管 Promise 对象最后状态如何,都会执行的操作。它与`done`方法的最大区别,它接受一个普通的回调函数作为参数,该函数不管怎样都必须执行。 -下面是一个例子,服务器使用Promise处理请求,然后使用`finally`方法关掉服务器。 +下面是一个例子,服务器使用 Promise 处理请求,然后使用`finally`方法关掉服务器。 ```javascript server.listen(0) @@ -803,7 +803,7 @@ Promise.prototype.finally = function (callback) { }; ``` -上面代码中,不管前面的Promise是`fulfilled`还是`rejected`,都会执行回调函数`callback`。 +上面代码中,不管前面的 Promise 是`fulfilled`还是`rejected`,都会执行回调函数`callback`。 ## 应用 @@ -822,9 +822,9 @@ const preloadImage = function (path) { }; ``` -### Generator函数与Promise的结合 +### Generator 函数与 Promise 的结合 -使用Generator函数管理流程,遇到异步操作的时候,通常返回一个`Promise`对象。 +使用 Generator 函数管理流程,遇到异步操作的时候,通常返回一个`Promise`对象。 ```javascript function getFoo () { @@ -861,7 +861,7 @@ function run (generator) { run(g); ``` -上面代码的Generator函数`g`之中,有一个异步操作`getFoo`,它返回的就是一个`Promise`对象。函数`run`用来处理这个`Promise`对象,并调用下一个`next`方法。 +上面代码的 Generator 函数`g`之中,有一个异步操作`getFoo`,它返回的就是一个`Promise`对象。函数`run`用来处理这个`Promise`对象,并调用下一个`next`方法。 ## Promise.try() @@ -976,4 +976,3 @@ Promise.try(database.users.get({id: userId})) ``` 事实上,`Promise.try`就是模拟`try`代码块,就像`promise.catch`模拟的是`catch`代码块。 - diff --git a/docs/proxy.md b/docs/proxy.md index 08dd66ce0..d890d6c8a 100644 --- a/docs/proxy.md +++ b/docs/proxy.md @@ -123,7 +123,7 @@ fproxy.foo === "Hello, foo" // true 对于可以设置、但没有设置拦截的操作,则直接落在目标对象上,按照原先的方式产生结果。 -下面是 Proxy 支持的拦截操作一览,一共13种。 +下面是 Proxy 支持的拦截操作一览,一共 13 种。 - **get(target, propKey, receiver)**:拦截对象属性的读取,比如`proxy.foo`和`proxy['foo']`。 - **set(target, propKey, value, receiver)**:拦截对象属性的设置,比如`proxy.foo = v`或`proxy['foo'] = v`,返回一个布尔值。 @@ -317,7 +317,7 @@ proxy.foo `set`方法用来拦截某个属性的赋值操作,可以接受四个参数,依次为目标对象、属性名、属性值和 Proxy 实例本身,其中最后一个参数可选。 -假定`Person`对象有一个`age`属性,该属性应该是一个不大于200的整数,那么可以使用`Proxy`保证`age`的属性值符合要求。 +假定`Person`对象有一个`age`属性,该属性应该是一个不大于 200 的整数,那么可以使用`Proxy`保证`age`的属性值符合要求。 ```javascript let validator = { @@ -1049,4 +1049,3 @@ function createWebService(baseUrl) { ``` 同理,Proxy 也可以用来实现数据库的 ORM 层。 - diff --git a/docs/reference.md b/docs/reference.md index fd4dcca88..a1878dcf7 100644 --- a/docs/reference.md +++ b/docs/reference.md @@ -2,46 +2,46 @@ ## 官方文件 -- [ECMAScript® 2015 Language Specification](http://www.ecma-international.org/ecma-262/6.0/index.html): ECMAScript 2015规格 -- [ECMAScript® 2016 Language Specification](http://www.ecma-international.org/ecma-262/7.0/): ECMAScript 2016规格 -- [ECMAScript® 2017 Language Specification](https://tc39.github.io/ecma262/):ECMAScript 2017规格(草案) -- [ECMAScript Current Proposals](https://github.com/tc39/ecma262): ECMAScript当前的所有提案 +- [ECMAScript® 2015 Language Specification](http://www.ecma-international.org/ecma-262/6.0/index.html): ECMAScript 2015 规格 +- [ECMAScript® 2016 Language Specification](http://www.ecma-international.org/ecma-262/7.0/): ECMAScript 2016 规格 +- [ECMAScript® 2017 Language Specification](https://tc39.github.io/ecma262/):ECMAScript 2017 规格(草案) +- [ECMAScript Current Proposals](https://github.com/tc39/ecma262): ECMAScript 当前的所有提案 - [ECMAScript Active Proposals](https://github.com/tc39/proposals): 已经进入正式流程的提案 -- [ECMAscript proposals](https://github.com/hemanth/es-next):从阶段0到阶段4的所有提案列表 +- [ECMAscript proposals](https://github.com/hemanth/es-next):从阶段 0 到阶段 4 的所有提案列表 - [TC39 meeting agendas](https://github.com/tc39/agendas): TC39 委员会历年的会议记录 -- [ECMAScript Daily](https://ecmascript-daily.github.io/): TC39委员会的动态 +- [ECMAScript Daily](https://ecmascript-daily.github.io/): TC39 委员会的动态 - [The TC39 Process](https://tc39.github.io/process-document/): 提案进入正式规格的流程 - [TC39: A Process Sketch, Stages 0 and 1](https://thefeedbackloop.xyz/tc39-a-process-sketch-stages-0-and-1/): Stage 0 和 Stage 1 的含义 - [TC39 Process Sketch, Stage 2](https://thefeedbackloop.xyz/tc39-process-sketch-stage-2/): Stage 2 的含义 ## 综合介绍 -- Axel Rauschmayer, [Exploring ES6: Upgrade to the next version of JavaScript](http://exploringjs.com/es6/): ES6的专著,本书的许多代码实例来自该书 +- Axel Rauschmayer, [Exploring ES6: Upgrade to the next version of JavaScript](http://exploringjs.com/es6/): ES6 的专著,本书的许多代码实例来自该书 - Sayanee Basu, [Use ECMAScript 6 Today](http://net.tutsplus.com/articles/news/ecmascript-6-today/) - Ariya Hidayat, [Toward Modern Web Apps with ECMAScript 6](http://www.sencha.com/blog/toward-modern-web-apps-with-ecmascript-6/) - Dale Schouten, [10 Ecmascript-6 tricks you can perform right now](http://html5hub.com/10-ecmascript-6-tricks-you-can-perform-right-now/) -- Colin Toh, [Lightweight ES6 Features That Pack A Punch](http://colintoh.com/blog/lightweight-es6-features): ES6的一些“轻量级”的特性介绍 +- Colin Toh, [Lightweight ES6 Features That Pack A Punch](http://colintoh.com/blog/lightweight-es6-features): ES6 的一些“轻量级”的特性介绍 - Domenic Denicola, [ES6: The Awesome Parts](http://www.slideshare.net/domenicdenicola/es6-the-awesome-parts) - Nicholas C. Zakas, [Understanding ECMAScript 6](https://github.com/nzakas/understandinges6) - Justin Drake, [ECMAScript 6 in Node.JS](https://github.com/JustinDrake/node-es6-examples) - Ryan Dao, [Summary of ECMAScript 6 major features](http://ryandao.net/portal/content/summary-ecmascript-6-major-features) -- Luke Hoban, [ES6 features](https://github.com/lukehoban/es6features): ES6新语法点的罗列 -- Traceur-compiler, [Language Features](https://github.com/google/traceur-compiler/wiki/LanguageFeatures): Traceur文档列出的一些ES6例子 -- Axel Rauschmayer, [ECMAScript 6: what’s next for JavaScript?](https://speakerdeck.com/rauschma/ecmascript-6-whats-next-for-javascript-august-2014): 关于ES6新增语法的综合介绍,有很多例子 -- Axel Rauschmayer, [Getting started with ECMAScript 6](http://www.2ality.com/2015/08/getting-started-es6.html): ES6语法点的综合介绍 +- Luke Hoban, [ES6 features](https://github.com/lukehoban/es6features): ES6 新语法点的罗列 +- Traceur-compiler, [Language Features](https://github.com/google/traceur-compiler/wiki/LanguageFeatures): Traceur 文档列出的一些 ES6 例子 +- Axel Rauschmayer, [ECMAScript 6: what’s next for JavaScript?](https://speakerdeck.com/rauschma/ecmascript-6-whats-next-for-javascript-august-2014): 关于 ES6 新增语法的综合介绍,有很多例子 +- Axel Rauschmayer, [Getting started with ECMAScript 6](http://www.2ality.com/2015/08/getting-started-es6.html): ES6 语法点的综合介绍 - Toby Ho, [ES6 in io.js](http://davidwalsh.name/es6-io) - Guillermo Rauch, [ECMAScript 6](http://rauchg.com/2015/ecmascript-6/) - Charles King, [The power of ECMAScript 6](http://charlesbking.com/power_of_es6/#/) -- Benjamin De Cock, [Frontend Guidelines](https://github.com/bendc/frontend-guidelines): ES6最佳实践 +- Benjamin De Cock, [Frontend Guidelines](https://github.com/bendc/frontend-guidelines): ES6 最佳实践 - Jani Hartikainen, [ES6: What are the benefits of the new features in practice?](http://codeutopia.net/blog/2015/01/06/es6-what-are-the-benefits-of-the-new-features-in-practice/) -- kangax, [Javascript quiz. ES6 edition](http://perfectionkills.com/javascript-quiz-es6/): ES6小测试 -- Jeremy Fairbank, [HTML5DevConf ES7 and Beyond!](https://speakerdeck.com/jfairbank/html5devconf-es7-and-beyond): ES7新增语法点介绍 +- kangax, [Javascript quiz. ES6 edition](http://perfectionkills.com/javascript-quiz-es6/): ES6 小测试 +- Jeremy Fairbank, [HTML5DevConf ES7 and Beyond!](https://speakerdeck.com/jfairbank/html5devconf-es7-and-beyond): ES7 新增语法点介绍 ## let 和 const -- Kyle Simpson, [For and against let](http://davidwalsh.name/for-and-against-let): 讨论let命令的作用域 -- kangax, [Why typeof is no longer “safe”](http://es-discourse.com/t/why-typeof-is-no-longer-safe/15): 讨论在块级作用域内,let命令的变量声明和赋值的行为 -- Axel Rauschmayer, [Variables and scoping in ECMAScript 6](http://www.2ality.com/2015/02/es6-scoping.html): 讨论块级作用域与let和const的行为 +- Kyle Simpson, [For and against let](http://davidwalsh.name/for-and-against-let): 讨论 let 命令的作用域 +- kangax, [Why typeof is no longer “safe”](http://es-discourse.com/t/why-typeof-is-no-longer-safe/15): 讨论在块级作用域内,let 命令的变量声明和赋值的行为 +- Axel Rauschmayer, [Variables and scoping in ECMAScript 6](http://www.2ality.com/2015/02/es6-scoping.html): 讨论块级作用域与 let 和 const 的行为 - Nicolas Bevacqua, [ES6 Let, Const and the “Temporal Dead Zone” (TDZ) in Depth](http://ponyfoo.com/articles/es6-let-const-and-temporal-dead-zone-in-depth) - acorn, [Function statements in strict mode](https://github.com/ternjs/acorn/issues/118): 块级作用域对严格模式的函数声明的影响 - Axel Rauschmayer, [ES proposal: global](http://www.2ality.com/2016/09/global.html): 顶层对象`global` @@ -62,8 +62,8 @@ ## 正则 -- Mathias Bynens, [Unicode-aware regular expressions in ES6](https://mathiasbynens.be/notes/es6-unicode-regex): 详细介绍正则表达式的u修饰符 -- Axel Rauschmayer, [New regular expression features in ECMAScript 6](http://www.2ality.com/2015/07/regexp-es6.html):ES6正则特性的详细介绍 +- Mathias Bynens, [Unicode-aware regular expressions in ES6](https://mathiasbynens.be/notes/es6-unicode-regex): 详细介绍正则表达式的 u 修饰符 +- Axel Rauschmayer, [New regular expression features in ECMAScript 6](http://www.2ality.com/2015/07/regexp-es6.html):ES6 正则特性的详细介绍 - Yang Guo, [RegExp lookbehind assertions](http://v8project.blogspot.jp/2016/02/regexp-lookbehind-assertions.html):介绍后行断言 - Axel Rauschmayer, [ES proposal: RegExp named capture groups](http://2ality.com/2017/05/regexp-named-capture-groups.html): 具名组匹配的介绍 @@ -74,8 +74,8 @@ ## 数组 -- Axel Rauschmayer, [ECMAScript 6’s new array methods](http://www.2ality.com/2014/05/es6-array-methods.html): 对ES6新增的数组方法的全面介绍 -- TC39, [Array.prototype.includes](https://github.com/tc39/Array.prototype.includes/): 数组的includes方法的规格 +- Axel Rauschmayer, [ECMAScript 6’s new array methods](http://www.2ality.com/2014/05/es6-array-methods.html): 对 ES6 新增的数组方法的全面介绍 +- TC39, [Array.prototype.includes](https://github.com/tc39/Array.prototype.includes/): 数组的 includes 方法的规格 - Axel Rauschmayer, [ECMAScript 6: holes in Arrays](http://www.2ality.com/2015/09/holes-arrays-es6.html): 数组的空位问题 ## 函数 @@ -84,18 +84,18 @@ - Jack Franklin, [Real Life ES6 - Arrow Functions](http://javascriptplayground.com/blog/2014/04/real-life-es6-arrow-fn/) - Axel Rauschmayer, [Handling required parameters in ECMAScript 6](http://www.2ality.com/2014/04/required-parameters-es6.html) - Dmitry Soshnikov, [ES6 Notes: Default values of parameters](http://dmitrysoshnikov.com/ecmascript/es6-notes-default-values-of-parameters/): 介绍参数的默认值 -- Ragan Wald, [Destructuring and Recursion in ES6](http://raganwald.com/2015/02/02/destructuring.html): rest参数和扩展运算符的详细介绍 -- Axel Rauschmayer, [The names of functions in ES6](http://www.2ality.com/2015/09/function-names-es6.html): 函数的name属性的详细介绍 -- Kyle Simpson, [Arrow This](http://blog.getify.com/arrow-this/): 箭头函数并没有自己的this -- Derick Bailey, [Do ES6 Arrow Functions Really Solve “this” In JavaScript?](http://derickbailey.com/2015/09/28/do-es6-arrow-functions-really-solve-this-in-javascript/):使用箭头函数处理this指向,必须非常小心 +- Ragan Wald, [Destructuring and Recursion in ES6](http://raganwald.com/2015/02/02/destructuring.html): rest 参数和扩展运算符的详细介绍 +- Axel Rauschmayer, [The names of functions in ES6](http://www.2ality.com/2015/09/function-names-es6.html): 函数的 name 属性的详细介绍 +- Kyle Simpson, [Arrow This](http://blog.getify.com/arrow-this/): 箭头函数并没有自己的 this +- Derick Bailey, [Do ES6 Arrow Functions Really Solve “this” In JavaScript?](http://derickbailey.com/2015/09/28/do-es6-arrow-functions-really-solve-this-in-javascript/):使用箭头函数处理 this 指向,必须非常小心 - Mark McDonnell, [Understanding recursion in functional JavaScript programming](http://www.integralist.co.uk/posts/js-recursion.html): 如何自己实现尾递归优化 - Nicholas C. Zakas, [The ECMAScript 2016 change you probably don't know](https://www.nczonline.net/blog/2016/10/the-ecmascript-2016-change-you-probably-dont-know/): 使用参数默认值时,不能在函数内部显式开启严格模式 - Axel Rauschmayer, [ES proposal: optional catch binding](http://2ality.com/2017/08/optional-catch-binding.html) ## 对象 -- Addy Osmani, [Data-binding Revolutions with Object.observe()](http://www.html5rocks.com/en/tutorials/es7/observe/): 介绍Object.observe()的概念 -- Sella Rafaeli, [Native JavaScript Data-Binding](http://www.sellarafaeli.com/blog/native_javascript_data_binding): 如何使用Object.observe方法,实现数据对象与DOM对象的双向绑定 +- Addy Osmani, [Data-binding Revolutions with Object.observe()](http://www.html5rocks.com/en/tutorials/es7/observe/): 介绍 Object.observe()的概念 +- Sella Rafaeli, [Native JavaScript Data-Binding](http://www.sellarafaeli.com/blog/native_javascript_data_binding): 如何使用 Object.observe 方法,实现数据对象与 DOM 对象的双向绑定 - Axel Rauschmayer, [`__proto__` in ECMAScript 6](http://www.2ality.com/2015/09/proto-es6.html) - Axel Rauschmayer, [Enumerability in ECMAScript 6](http://www.2ality.com/2015/10/enumerability-es6.html) - Axel Rauschmayer, [ES proposal: Object.getOwnPropertyDescriptors()](http://www.2ality.com/2016/02/object-getownpropertydescriptors.html) @@ -103,29 +103,29 @@ ## Symbol -- Axel Rauschmayer, [Symbols in ECMAScript 6](http://www.2ality.com/2014/12/es6-symbols.html): Symbol简介 -- MDN, [Symbol](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Symbol): Symbol类型的详细介绍 +- Axel Rauschmayer, [Symbols in ECMAScript 6](http://www.2ality.com/2014/12/es6-symbols.html): Symbol 简介 +- MDN, [Symbol](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Symbol): Symbol 类型的详细介绍 - Jason Orendorff, [ES6 In Depth: Symbols](https://hacks.mozilla.org/2015/06/es6-in-depth-symbols/) -- Keith Cirkel, [Metaprogramming in ES6: Symbols and why they're awesome](http://blog.keithcirkel.co.uk/metaprogramming-in-es6-symbols/): Symbol的深入介绍 +- Keith Cirkel, [Metaprogramming in ES6: Symbols and why they're awesome](http://blog.keithcirkel.co.uk/metaprogramming-in-es6-symbols/): Symbol 的深入介绍 - Axel Rauschmayer, [Customizing ES6 via well-known symbols](http://www.2ality.com/2015/09/well-known-symbols-es6.html) - Derick Bailey, [Creating A True Singleton In Node.js, With ES6 Symbols](https://derickbailey.com/2016/03/09/creating-a-true-singleton-in-node-js-with-es6-symbols/) - Das Surma, [How to read web specs Part IIa – Or: ECMAScript Symbols](https://dassur.ma/things/reading-specs-2/): 介绍 Symbol 的规格 -## Set和Map +## Set 和 Map -- Mozilla Developer Network, [WeakSet](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/WeakSet):介绍WeakSet数据结构 -- Dwayne Charrington, [What Are Weakmaps In ES6?](http://ilikekillnerds.com/2015/02/what-are-weakmaps-in-es6/): WeakMap数据结构介绍 -- Axel Rauschmayer, [ECMAScript 6: maps and sets](http://www.2ality.com/2015/01/es6-maps-sets.html): Set和Map结构的详细介绍 -- Jason Orendorff, [ES6 In Depth: Collections](https://hacks.mozilla.org/2015/06/es6-in-depth-collections/):Set和Map结构的设计思想 -- Axel Rauschmayer, [Converting ES6 Maps to and from JSON](http://www.2ality.com/2015/08/es6-map-json.html): 如何将Map与其他数据结构互相转换 +- Mozilla Developer Network, [WeakSet](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/WeakSet):介绍 WeakSet 数据结构 +- Dwayne Charrington, [What Are Weakmaps In ES6?](http://ilikekillnerds.com/2015/02/what-are-weakmaps-in-es6/): WeakMap 数据结构介绍 +- Axel Rauschmayer, [ECMAScript 6: maps and sets](http://www.2ality.com/2015/01/es6-maps-sets.html): Set 和 Map 结构的详细介绍 +- Jason Orendorff, [ES6 In Depth: Collections](https://hacks.mozilla.org/2015/06/es6-in-depth-collections/):Set 和 Map 结构的设计思想 +- Axel Rauschmayer, [Converting ES6 Maps to and from JSON](http://www.2ality.com/2015/08/es6-map-json.html): 如何将 Map 与其他数据结构互相转换 ## Proxy 和 Reflect - Nicholas C. Zakas, [Creating defensive objects with ES6 proxies](http://www.nczonline.net/blog/2014/04/22/creating-defensive-objects-with-es6-proxies/) -- Axel Rauschmayer, [Meta programming with ECMAScript 6 proxies](http://www.2ality.com/2014/12/es6-proxies.html): Proxy详解 -- Daniel Zautner, [Meta-programming JavaScript Using Proxies](http://dzautner.com/meta-programming-javascript-using-proxies/): 使用Proxy实现元编程 -- Tom Van Cutsem, [Harmony-reflect](https://github.com/tvcutsem/harmony-reflect/wiki): Reflect对象的设计目的 -- Tom Van Cutsem, [Proxy Traps](https://github.com/tvcutsem/harmony-reflect/blob/master/doc/traps.md): Proxy拦截操作一览 +- Axel Rauschmayer, [Meta programming with ECMAScript 6 proxies](http://www.2ality.com/2014/12/es6-proxies.html): Proxy 详解 +- Daniel Zautner, [Meta-programming JavaScript Using Proxies](http://dzautner.com/meta-programming-javascript-using-proxies/): 使用 Proxy 实现元编程 +- Tom Van Cutsem, [Harmony-reflect](https://github.com/tvcutsem/harmony-reflect/wiki): Reflect 对象的设计目的 +- Tom Van Cutsem, [Proxy Traps](https://github.com/tvcutsem/harmony-reflect/blob/master/doc/traps.md): Proxy 拦截操作一览 - Tom Van Cutsem, [Reflect API](https://github.com/tvcutsem/harmony-reflect/blob/master/doc/api.md) - Tom Van Cutsem, [Proxy Handler API](https://github.com/tvcutsem/harmony-reflect/blob/master/doc/handler_api.md) - Nicolas Bevacqua, [ES6 Proxies in Depth](http://ponyfoo.com/articles/es6-proxies-in-depth) @@ -139,9 +139,9 @@ - Jake Archibald, [JavaScript Promises: There and back again](http://www.html5rocks.com/en/tutorials/es6/promises/) - Tilde, [rsvp.js](https://github.com/tildeio/rsvp.js) -- Sandeep Panda, [An Overview of JavaScript Promises](http://www.sitepoint.com/overview-javascript-promises/): ES6 Promise入门介绍 -- Dave Atchley, [ES6 Promises](http://www.datchley.name/es6-promises/): Promise的语法介绍 -- Axel Rauschmayer, [ECMAScript 6 promises (2/2): the API](http://www.2ality.com/2014/10/es6-promises-api.html): 对ES6 Promise规格和用法的详细介绍 +- Sandeep Panda, [An Overview of JavaScript Promises](http://www.sitepoint.com/overview-javascript-promises/): ES6 Promise 入门介绍 +- Dave Atchley, [ES6 Promises](http://www.datchley.name/es6-promises/): Promise 的语法介绍 +- Axel Rauschmayer, [ECMAScript 6 promises (2/2): the API](http://www.2ality.com/2014/10/es6-promises-api.html): 对 ES6 Promise 规格和用法的详细介绍 - Jack Franklin, [Embracing Promises in JavaScript](http://javascriptplayground.com/blog/2015/02/promises/): catch 方法的例子 - Ronald Chen, [How to escape Promise Hell](https://medium.com/@pyrolistical/how-to-get-out-of-promise-hell-8c20e0ab0513#.2an1he6vf): 如何使用`Promise.all`方法的一些很好的例子 - Jordan Harband, [proposal-promise-try](https://github.com/ljharb/proposal-promise-try): Promise.try() 方法的提案 @@ -152,9 +152,9 @@ - Mozilla Developer Network, [Iterators and generators](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Iterators_and_Generators) - Mozilla Developer Network, [The Iterator protocol](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/The_Iterator_protocol) -- Jason Orendorff, [ES6 In Depth: Iterators and the for-of loop](https://hacks.mozilla.org/2015/04/es6-in-depth-iterators-and-the-for-of-loop/): 遍历器与for...of循环的介绍 -- Axel Rauschmayer, [Iterators and generators in ECMAScript 6](http://www.2ality.com/2013/06/iterators-generators.html): 探讨Iterator和Generator的设计目的 -- Axel Rauschmayer, [Iterables and iterators in ECMAScript 6](http://www.2ality.com/2015/02/es6-iteration.html): Iterator的详细介绍 +- Jason Orendorff, [ES6 In Depth: Iterators and the for-of loop](https://hacks.mozilla.org/2015/04/es6-in-depth-iterators-and-the-for-of-loop/): 遍历器与 for...of 循环的介绍 +- Axel Rauschmayer, [Iterators and generators in ECMAScript 6](http://www.2ality.com/2013/06/iterators-generators.html): 探讨 Iterator 和 Generator 的设计目的 +- Axel Rauschmayer, [Iterables and iterators in ECMAScript 6](http://www.2ality.com/2015/02/es6-iteration.html): Iterator 的详细介绍 - Kyle Simpson, [Iterating ES6 Numbers](http://blog.getify.com/iterating-es6-numbers/): 在数值对象上部署遍历器 ## Generator @@ -162,55 +162,55 @@ - Matt Baker, [Replacing callbacks with ES6 Generators](http://flippinawesome.org/2014/02/10/replacing-callbacks-with-es6-generators/) - Steven Sanderson, [Experiments with Koa and JavaScript Generators](http://blog.stevensanderson.com/2013/12/21/experiments-with-koa-and-javascript-generators/) - jmar777, [What's the Big Deal with Generators?](http://devsmash.com/blog/whats-the-big-deal-with-generators) -- Marc Harter, [Generators in Node.js: Common Misconceptions and Three Good Use Cases](http://strongloop.com/strongblog/how-to-generators-node-js-yield-use-cases/): 讨论Generator函数的作用 -- StackOverflow, [ES6 yield : what happens to the arguments of the first call next()?](http://stackoverflow.com/questions/20977379/es6-yield-what-happens-to-the-arguments-of-the-first-call-next): 第一次使用next方法时不能带有参数 -- Kyle Simpson, [ES6 Generators: Complete Series](http://davidwalsh.name/es6-generators): 由浅入深探讨Generator的系列文章,共四篇 -- Gajus Kuizinas, [The Definitive Guide to the JavaScript Generators](http://gajus.com/blog/2/the-definetive-guide-to-the-javascript-generators): 对Generator的综合介绍 -- Jan Krems, [Generators Are Like Arrays](https://gist.github.com/jkrems/04a2b34fb9893e4c2b5c): 讨论Generator可以被当作数据结构看待 -- Harold Cooper, [Coroutine Event Loops in Javascript](http://syzygy.st/javascript-coroutines/): Generator用于实现状态机 -- Ruslan Ismagilov, [learn-generators](https://github.com/isRuslan/learn-generators): 编程练习,共6道题 -- Steven Sanderson, [Experiments with Koa and JavaScript Generators](http://blog.stevensanderson.com/2013/12/21/experiments-with-koa-and-javascript-generators/): Generator入门介绍,以Koa框架为例 -- Mahdi Dibaiee, [ES7 Array and Generator comprehensions](http://dibaiee.ir/es7-array-generator-comprehensions/):ES7的Generator推导 +- Marc Harter, [Generators in Node.js: Common Misconceptions and Three Good Use Cases](http://strongloop.com/strongblog/how-to-generators-node-js-yield-use-cases/): 讨论 Generator 函数的作用 +- StackOverflow, [ES6 yield : what happens to the arguments of the first call next()?](http://stackoverflow.com/questions/20977379/es6-yield-what-happens-to-the-arguments-of-the-first-call-next): 第一次使用 next 方法时不能带有参数 +- Kyle Simpson, [ES6 Generators: Complete Series](http://davidwalsh.name/es6-generators): 由浅入深探讨 Generator 的系列文章,共四篇 +- Gajus Kuizinas, [The Definitive Guide to the JavaScript Generators](http://gajus.com/blog/2/the-definetive-guide-to-the-javascript-generators): 对 Generator 的综合介绍 +- Jan Krems, [Generators Are Like Arrays](https://gist.github.com/jkrems/04a2b34fb9893e4c2b5c): 讨论 Generator 可以被当作数据结构看待 +- Harold Cooper, [Coroutine Event Loops in Javascript](http://syzygy.st/javascript-coroutines/): Generator 用于实现状态机 +- Ruslan Ismagilov, [learn-generators](https://github.com/isRuslan/learn-generators): 编程练习,共 6 道题 +- Steven Sanderson, [Experiments with Koa and JavaScript Generators](http://blog.stevensanderson.com/2013/12/21/experiments-with-koa-and-javascript-generators/): Generator 入门介绍,以 Koa 框架为例 +- Mahdi Dibaiee, [ES7 Array and Generator comprehensions](http://dibaiee.ir/es7-array-generator-comprehensions/):ES7 的 Generator 推导 - Nicolas Bevacqua, [ES6 Generators in Depth](http://ponyfoo.com/articles/es6-generators-in-depth) -- Axel Rauschmayer, [ES6 generators in depth](http://www.2ality.com/2015/03/es6-generators.html): Generator规格的详尽讲解 +- Axel Rauschmayer, [ES6 generators in depth](http://www.2ality.com/2015/03/es6-generators.html): Generator 规格的详尽讲解 - Derick Bailey, [Using ES6 Generators To Short-Circuit Hierarchical Data Iteration](https://derickbailey.com/2015/10/05/using-es6-generators-to-short-circuit-hierarchical-data-iteration/):使用 for...of 循环完成预定的操作步骤 -## 异步操作和Async函数 +## 异步操作和 Async 函数 -- Luke Hoban, [Async Functions for ECMAScript](https://github.com/lukehoban/ecmascript-asyncawait): Async函数的设计思想,与Promise、Gernerator函数的关系 -- Jafar Husain, [Asynchronous Generators for ES7](https://github.com/jhusain/asyncgenerator): Async函数的深入讨论 -- Nolan Lawson, [Taming the asynchronous beast with ES7](http://pouchdb.com/2015/03/05/taming-the-async-beast-with-es7.html): async函数通俗的实例讲解 -- Jafar Husain, [Async Generators](https://docs.google.com/file/d/0B4PVbLpUIdzoMDR5dWstRllXblU/view?sle=true): 对async与Generator混合使用的一些讨论 -- Daniel Brain, [Understand promises before you start using async/await](https://medium.com/@bluepnume/learn-about-promises-before-you-start-using-async-await-eb148164a9c8): 讨论async/await与Promise的关系 +- Luke Hoban, [Async Functions for ECMAScript](https://github.com/lukehoban/ecmascript-asyncawait): Async 函数的设计思想,与 Promise、Gernerator 函数的关系 +- Jafar Husain, [Asynchronous Generators for ES7](https://github.com/jhusain/asyncgenerator): Async 函数的深入讨论 +- Nolan Lawson, [Taming the asynchronous beast with ES7](http://pouchdb.com/2015/03/05/taming-the-async-beast-with-es7.html): async 函数通俗的实例讲解 +- Jafar Husain, [Async Generators](https://docs.google.com/file/d/0B4PVbLpUIdzoMDR5dWstRllXblU/view?sle=true): 对 async 与 Generator 混合使用的一些讨论 +- Daniel Brain, [Understand promises before you start using async/await](https://medium.com/@bluepnume/learn-about-promises-before-you-start-using-async-await-eb148164a9c8): 讨论 async/await 与 Promise 的关系 - Jake Archibald, [Async functions - making promises friendly](https://developers.google.com/web/fundamentals/getting-started/primers/async-functions) - Axel Rauschmayer, [ES proposal: asynchronous iteration](http://www.2ality.com/2016/10/asynchronous-iteration.html): 异步遍历器的详细介绍 - Dima Grossman, [How to write async await without try-catch blocks in Javascript](http://blog.grossman.io/how-to-write-async-await-without-try-catch-blocks-in-javascript/): 除了 try/catch 以外的 async 函数内部捕捉错误的方法 ## Class -- Sebastian Porto, [ES6 classes and JavaScript prototypes](https://reinteractive.net/posts/235-es6-classes-and-javascript-prototypes): ES6 Class的写法与ES5 Prototype的写法对比 -- Jack Franklin, [An introduction to ES6 classes](http://javascriptplayground.com/blog/2014/07/introduction-to-es6-classes-tutorial/): ES6 class的入门介绍 +- Sebastian Porto, [ES6 classes and JavaScript prototypes](https://reinteractive.net/posts/235-es6-classes-and-javascript-prototypes): ES6 Class 的写法与 ES5 Prototype 的写法对比 +- Jack Franklin, [An introduction to ES6 classes](http://javascriptplayground.com/blog/2014/07/introduction-to-es6-classes-tutorial/): ES6 class 的入门介绍 - Axel Rauschmayer, [ECMAScript 6: new OOP features besides classes](http://www.2ality.com/2014/12/es6-oop.html) -- Axel Rauschmayer, [Classes in ECMAScript 6 (final semantics)](http://www.2ality.com/2015/02/es6-classes-final.html): Class语法的详细介绍和设计思想分析 -- Eric Faust, [ES6 In Depth: Subclassing](https://hacks.mozilla.org/2015/08/es6-in-depth-subclassing/): Class语法的深入介绍 -- Nicolás Bevacqua, [Binding Methods to Class Instance Objects](https://ponyfoo.com/articles/binding-methods-to-class-instance-objects): 如何绑定类的实例中的this +- Axel Rauschmayer, [Classes in ECMAScript 6 (final semantics)](http://www.2ality.com/2015/02/es6-classes-final.html): Class 语法的详细介绍和设计思想分析 +- Eric Faust, [ES6 In Depth: Subclassing](https://hacks.mozilla.org/2015/08/es6-in-depth-subclassing/): Class 语法的深入介绍 +- Nicolás Bevacqua, [Binding Methods to Class Instance Objects](https://ponyfoo.com/articles/binding-methods-to-class-instance-objects): 如何绑定类的实例中的 this ## Decorator -- Maximiliano Fierro, [Declarative vs Imperative](http://elmasse.github.io/js/decorators-bindings-es7.html): Decorators和Mixin介绍 -- Justin Fagnani, ["Real" Mixins with JavaScript Classes](http://justinfagnani.com/2015/12/21/real-mixins-with-javascript-classes/): 使用类的继承实现Mixin -- Addy Osmani, [Exploring ES2016 Decorators](https://medium.com/google-developers/exploring-es7-decorators-76ecb65fb841): Decorator的深入介绍 +- Maximiliano Fierro, [Declarative vs Imperative](http://elmasse.github.io/js/decorators-bindings-es7.html): Decorators 和 Mixin 介绍 +- Justin Fagnani, ["Real" Mixins with JavaScript Classes](http://justinfagnani.com/2015/12/21/real-mixins-with-javascript-classes/): 使用类的继承实现 Mixin +- Addy Osmani, [Exploring ES2016 Decorators](https://medium.com/google-developers/exploring-es7-decorators-76ecb65fb841): Decorator 的深入介绍 - Sebastian McKenzie, [Allow decorators for functions as well](https://github.com/wycats/javascript-decorators/issues/4): 为什么修饰器不能用于函数 -- Maximiliano Fierro, [Traits with ES7 Decorators](http://cocktailjs.github.io/blog/traits-with-es7-decorators.html): Trait的用法介绍 +- Maximiliano Fierro, [Traits with ES7 Decorators](http://cocktailjs.github.io/blog/traits-with-es7-decorators.html): Trait 的用法介绍 - Jonathan Creamer: [Using ES2016 Decorators to Publish on an Event Bus](http://jonathancreamer.com/using-es2016-decorators-to-publish-on-an-event-bus/): 使用修饰器实现自动发布事件 ## Module -- Jack Franklin, [JavaScript Modules the ES6 Way](http://24ways.org/2014/javascript-modules-the-es6-way/): ES6模块入门 -- Axel Rauschmayer, [ECMAScript 6 modules: the final syntax](http://www.2ality.com/2014/09/es6-modules-final.html): ES6模块的介绍,以及与CommonJS规格的详细比较 -- Dave Herman, [Static module resolution](http://calculist.org/blog/2012/06/29/static-module-resolution/): ES6模块的静态化设计思想 -- Jason Orendorff, [ES6 In Depth: Modules](https://hacks.mozilla.org/2015/08/es6-in-depth-modules/): ES6模块设计思想的介绍 -- Ben Newman, [The Importance of import and export](http://benjamn.github.io/empirenode-2015/#/): ES6模块的设计思想 +- Jack Franklin, [JavaScript Modules the ES6 Way](http://24ways.org/2014/javascript-modules-the-es6-way/): ES6 模块入门 +- Axel Rauschmayer, [ECMAScript 6 modules: the final syntax](http://www.2ality.com/2014/09/es6-modules-final.html): ES6 模块的介绍,以及与 CommonJS 规格的详细比较 +- Dave Herman, [Static module resolution](http://calculist.org/blog/2012/06/29/static-module-resolution/): ES6 模块的静态化设计思想 +- Jason Orendorff, [ES6 In Depth: Modules](https://hacks.mozilla.org/2015/08/es6-in-depth-modules/): ES6 模块设计思想的介绍 +- Ben Newman, [The Importance of import and export](http://benjamn.github.io/empirenode-2015/#/): ES6 模块的设计思想 - ESDiscuss, [Why is "export default var a = 1;" invalid syntax?](https://esdiscuss.org/topic/why-is-export-default-var-a-1-invalid-syntax) - Bradley Meck, [ES6 Module Interoperability](https://github.com/nodejs/node-eps/blob/master/002-es6-modules.md): 介绍 Node 如何处理 ES6 语法加载 CommonJS 模块 - Axel Rauschmayer, [Making transpiled ES modules more spec-compliant](http://www.2ality.com/2017/01/babel-esm-spec-mode.html): ES6 模块编译成 CommonJS 模块的详细介绍 @@ -221,7 +221,7 @@ - Ilmari Heikkinen, [Typed Arrays: Binary Data in the Browser](http://www.html5rocks.com/en/tutorials/webgl/typed_arrays/) - Khronos, [Typed Array Specification](http://www.khronos.org/registry/typedarray/specs/latest/) -- Ian Elliot, [Reading A BMP File In JavaScript](http://www.i-programmer.info/projects/36-web/6234-reading-a-bmp-file-in-javascript.html) +- Ian Elliot, [Reading A BMP File In JavaScript](http://www.i-programmer.info/projects/36-web/6234-reading-a-bmp-file-in-javascript.html) - Renato Mangini, [How to convert ArrayBuffer to and from String](http://updates.html5rocks.com/2012/06/How-to-convert-ArrayBuffer-to-and-from-String) - Axel Rauschmayer, [Typed Arrays in ECMAScript 6](http://www.2ality.com/2015/09/typed-arrays.html) - Axel Rauschmayer, [ES proposal: Shared memory and atomics](http://2ality.com/2017/01/shared-array-buffer.html) @@ -237,16 +237,15 @@ ## 工具 -- Babel, [Babel Handbook](https://github.com/thejameskyle/babel-handbook/tree/master/translations/en): Babel的用法介绍 -- Google, [traceur-compiler](https://github.com/google/traceur-compiler): Traceur编译器 +- Babel, [Babel Handbook](https://github.com/thejameskyle/babel-handbook/tree/master/translations/en): Babel 的用法介绍 +- Google, [traceur-compiler](https://github.com/google/traceur-compiler): Traceur 编译器 - Casper Beyer, [ECMAScript 6 Features and Tools](http://caspervonb.github.io/2014/03/05/ecmascript6-features-and-tools.html) - Stoyan Stefanov, [Writing ES6 today with jstransform](http://www.phpied.com/writing-es6-today-with-jstransform/) -- ES6 Module Loader, [ES6 Module Loader Polyfill](https://github.com/ModuleLoader/es6-module-loader): 在浏览器和node.js加载ES6模块的一个库,文档里对ES6模块有详细解释 -- Paul Miller, [es6-shim](https://github.com/paulmillr/es6-shim): 一个针对老式浏览器,模拟ES6部分功能的垫片库(shim) -- army8735, [Javascript Downcast](https://github.com/army8735/jsdc): 国产的ES6到ES5的转码器 -- esnext, [ES6 Module Transpiler](https://github.com/esnext/es6-module-transpiler):基于node.js的将ES6模块转为ES5代码的命令行工具 -- Sebastian McKenzie, [BabelJS](http://babeljs.io/): ES6转译器 +- ES6 Module Loader, [ES6 Module Loader Polyfill](https://github.com/ModuleLoader/es6-module-loader): 在浏览器和 node.js 加载 ES6 模块的一个库,文档里对 ES6 模块有详细解释 +- Paul Miller, [es6-shim](https://github.com/paulmillr/es6-shim): 一个针对老式浏览器,模拟 ES6 部分功能的垫片库(shim) +- army8735, [Javascript Downcast](https://github.com/army8735/jsdc): 国产的 ES6 到 ES5 的转码器 +- esnext, [ES6 Module Transpiler](https://github.com/esnext/es6-module-transpiler):基于 node.js 的将 ES6 模块转为 ES5 代码的命令行工具 +- Sebastian McKenzie, [BabelJS](http://babeljs.io/): ES6 转译器 - SystemJS, [SystemJS](https://github.com/systemjs/systemjs): 在浏览器中加载 AMD、CJS、ES6 模块的一个垫片库 - Modernizr, [HTML5 Cross Browser Polyfills](https://github.com/Modernizr/Modernizr/wiki/HTML5-Cross-Browser-Polyfills#ecmascript-6-harmony): ES6 垫片库清单 - Facebook, [regenerator](https://github.com/facebook/regenerator): 将 Generator 函数转为 ES5 的转码器 - diff --git a/docs/reflect.md b/docs/reflect.md index 3ac48933e..6cd9c38af 100644 --- a/docs/reflect.md +++ b/docs/reflect.md @@ -84,7 +84,7 @@ Reflect.apply(Math.floor, undefined, [1.75]) // 1 ## 静态方法 -`Reflect`对象一共有13个静态方法。 +`Reflect`对象一共有 13 个静态方法。 - Reflect.apply(target, thisArg, args) - Reflect.construct(target, args) @@ -517,4 +517,3 @@ function set(target, key, value, receiver) { ``` 上面代码中,先定义了一个`Set`集合,所有观察者函数都放进这个集合。然后,`observable`函数返回原始对象的代理,拦截赋值操作。拦截函数`set`之中,会自动执行所有观察者。 - diff --git a/docs/regex.md b/docs/regex.md index 6a7e216a8..91a57dae5 100644 --- a/docs/regex.md +++ b/docs/regex.md @@ -38,9 +38,9 @@ new RegExp(/abc/ig, 'i').flags ## 字符串的正则方法 -字符串对象共有4个方法,可以使用正则表达式:`match()`、`replace()`、`search()`和`split()`。 +字符串对象共有 4 个方法,可以使用正则表达式:`match()`、`replace()`、`search()`和`split()`。 -ES6 将这4个方法,在语言内部全部调用`RegExp`的实例方法,从而做到所有与正则相关的方法,全都定义在`RegExp`对象上。 +ES6 将这 4 个方法,在语言内部全部调用`RegExp`的实例方法,从而做到所有与正则相关的方法,全都定义在`RegExp`对象上。 - `String.prototype.match` 调用 `RegExp.prototype[Symbol.match]` - `String.prototype.replace` 调用 `RegExp.prototype[Symbol.replace]` @@ -49,7 +49,7 @@ ES6 将这4个方法,在语言内部全部调用`RegExp`的实例方法,从 ## u 修饰符 -ES6 对正则表达式添加了`u`修饰符,含义为“Unicode模式”,用来正确处理大于`\uFFFF`的 Unicode 字符。也就是说,会正确处理四个字节的 UTF-16 编码。 +ES6 对正则表达式添加了`u`修饰符,含义为“Unicode 模式”,用来正确处理大于`\uFFFF`的 Unicode 字符。也就是说,会正确处理四个字节的 UTF-16 编码。 ```javascript /^\uD83D/u.test('\uD83D\uDC2A') // false @@ -83,7 +83,7 @@ ES6 新增了使用大括号表示 Unicode 字符,这种表示法在正则表 /\u{20BB7}/u.test('𠮷') // true ``` -上面代码表示,如果不加`u`修饰符,正则表达式无法识别`\u{61}`这种表示法,只会认为这匹配61个连续的`u`。 +上面代码表示,如果不加`u`修饰符,正则表达式无法识别`\u{61}`这种表示法,只会认为这匹配 61 个连续的`u`。 **(3)量词** @@ -358,7 +358,7 @@ re.flags // 's' ## 后行断言 -JavaScript 语言的正则表达式,只支持先行断言(lookahead)和先行否定断言(negative lookahead),不支持后行断言(lookbehind)和后行否定断言(negative lookbehind)。目前,有一个[提案](https://github.com/goyakin/es-regexp-lookbehind),引入后行断言,V8 引擎4.9版已经支持。 +JavaScript 语言的正则表达式,只支持先行断言(lookahead)和先行否定断言(negative lookahead),不支持后行断言(lookbehind)和后行否定断言(negative lookbehind)。目前,有一个[提案](https://github.com/goyakin/es-regexp-lookbehind),引入后行断言,V8 引擎 4.9 版已经支持。 ”先行断言“指的是,`x`只有在`y`前面才匹配,必须写成`/x(?=y)/`。比如,只匹配百分号之前的数字,要写成`/\d+(?=%)/`。”先行否定断言“指的是,`x`只有不在`y`前面才匹配,必须写成`/x(?!y)/`。比如,只匹配不在百分号之前的数字,要写成`/\d+(?!%)/`。 @@ -500,7 +500,7 @@ const month = matchObj.groups.month; // 12 const day = matchObj.groups.day; // 31 ``` -上面代码中,“具名组匹配”在圆括号内部,模式的头部添加“问号 + 尖括号 + 组名”(`?`),然后就可以在`exec`方法返回结果的`groups`属性上引用该组名。同时,数字序号(` matchObj[1]`)依然有效。 +上面代码中,“具名组匹配”在圆括号内部,模式的头部添加“问号 + 尖括号 + 组名”(`?`),然后就可以在`exec`方法返回结果的`groups`属性上引用该组名。同时,数字序号(`matchObj[1]`)依然有效。 具名组匹配等于为每一组匹配加上了 ID,便于描述匹配的目的。如果组的顺序变了,也不用改变匹配后的处理代码。 @@ -581,4 +581,3 @@ const RE_TWICE = /^(?[a-z]+)!\k!\1$/; RE_TWICE.test('abc!abc!abc') // true RE_TWICE.test('abc!abc!ab') // false ``` - diff --git a/docs/set-map.md b/docs/set-map.md index df0fe16fb..a8753cfbe 100644 --- a/docs/set-map.md +++ b/docs/set-map.md @@ -1,4 +1,4 @@ -# Set和Map数据结构 +# Set 和 Map 数据结构 ## Set @@ -91,7 +91,7 @@ Set 结构的实例有以下属性。 Set 实例的方法分为两大类:操作方法(用于操作数据)和遍历方法(用于遍历成员)。下面先介绍四个操作方法。 -- `add(value)`:添加某个值,返回Set结构本身。 +- `add(value)`:添加某个值,返回 Set 结构本身。 - `delete(value)`:删除某个值,返回一个布尔值,表示删除是否成功。 - `has(value)`:返回一个布尔值,表示该值是否为`Set`的成员。 - `clear()`:清除所有成员,没有返回值。 @@ -162,7 +162,7 @@ Set 结构的实例有四个遍历方法,可以用于遍历成员。 - `entries()`:返回键值对的遍历器 - `forEach()`:使用回调函数遍历每个成员 -需要特别指出的是,`Set`的遍历顺序就是插入顺序。这个特性有时非常有用,比如使用Set保存一个回调函数列表,调用时就能保证按照添加顺序调用。 +需要特别指出的是,`Set`的遍历顺序就是插入顺序。这个特性有时非常有用,比如使用 Set 保存一个回调函数列表,调用时就能保证按照添加顺序调用。 **(1)`keys()`,`values()`,`entries()`** @@ -373,7 +373,7 @@ ws.delete(window); ws.has(window); // false ``` -WeakSet没有`size`属性,没有办法遍历它的成员。 +WeakSet 没有`size`属性,没有办法遍历它的成员。 ```javascript ws.size // undefined @@ -403,7 +403,7 @@ class Foo { } ``` -上面代码保证了`Foo`的实例方法,只能在`Foo`的实例上调用。这里使用WeakSet的好处是,`foos`对实例的引用,不会被计入内存回收机制,所以删除实例的时候,不用考虑`foos`,也不会出现内存泄漏。 +上面代码保证了`Foo`的实例方法,只能在`Foo`的实例上调用。这里使用 WeakSet 的好处是,`foos`对实例的引用,不会被计入内存回收机制,所以删除实例的时候,不用考虑`foos`,也不会出现内存泄漏。 ## Map @@ -421,7 +421,7 @@ data['[object HTMLDivElement]'] // "metadata" 上面代码原意是将一个 DOM 节点作为对象`data`的键,但是由于对象只接受字符串作为键名,所以`element`被自动转为字符串`[object HTMLDivElement]`。 -为了解决这个问题,ES6 提供了 Map 数据结构。它类似于对象,也是键值对的集合,但是“键”的范围不限于字符串,各种类型的值(包括对象)都可以当作键。也就是说,Object 结构提供了“字符串—值”的对应,Map结构提供了“值—值”的对应,是一种更完善的 Hash 结构实现。如果你需要“键值对”的数据结构,Map 比 Object 更合适。 +为了解决这个问题,ES6 提供了 Map 数据结构。它类似于对象,也是键值对的集合,但是“键”的范围不限于字符串,各种类型的值(包括对象)都可以当作键。也就是说,Object 结构提供了“字符串—值”的对应,Map 结构提供了“值—值”的对应,是一种更完善的 Hash 结构实现。如果你需要“键值对”的数据结构,Map 比 Object 更合适。 ```javascript const m = new Map(); @@ -562,7 +562,7 @@ map.get(NaN) // 123 Map 结构的实例有以下属性和操作方法。 -**(1)size属性** +**(1)size 属性** `size`属性返回 Map 结构的成员总数。 @@ -877,7 +877,7 @@ jsonToStrMap('{"yes": true, "no": false}') // Map {'yes' => true, 'no' => false} ``` -但是,有一种特殊情况,整个 JSON 就是一个数组,且每个数组成员本身,又是一个有两个成员的数组。这时,它可以一一对应地转为Map。这往往是数组转为 JSON 的逆操作。 +但是,有一种特殊情况,整个 JSON 就是一个数组,且每个数组成员本身,又是一个有两个成员的数组。这时,它可以一一对应地转为 Map。这往往是数组转为 JSON 的逆操作。 ```javascript function jsonToMap(jsonStr) { diff --git a/docs/simd.md b/docs/simd.md index 5ecb388a1..01202d358 100644 --- a/docs/simd.md +++ b/docs/simd.md @@ -4,9 +4,9 @@ SIMD(发音`/sim-dee/`)是“Single Instruction/Multiple Data”的缩写,意为“单指令,多数据”。它是 JavaScript 操作 CPU 对应指令的接口,你可以看做这是一种不同的运算执行模式。与它相对的是 SISD(“Single Instruction/Single Data”),即“单指令,单数据”。 -SIMD 的含义是使用一个指令,完成多个数据的运算;SISD 的含义是使用一个指令,完成单个数据的运算,这是 JavaScript 的默认运算模式。显而易见,SIMD 的执行效率要高于 SISD,所以被广泛用于3D图形运算、物理模拟等运算量超大的项目之中。 +SIMD 的含义是使用一个指令,完成多个数据的运算;SISD 的含义是使用一个指令,完成单个数据的运算,这是 JavaScript 的默认运算模式。显而易见,SIMD 的执行效率要高于 SISD,所以被广泛用于 3D 图形运算、物理模拟等运算量超大的项目之中。 -为了理解SIMD,请看下面的例子。 +为了理解 SIMD,请看下面的例子。 ```javascript var a = [1, 2, 3, 4]; @@ -20,7 +20,7 @@ c[3] = a[3] + b[3]; c // Array[6, 8, 10, 12] ``` -上面代码中,数组`a`和`b`的对应成员相加,结果放入数组`c`。它的运算模式是依次处理每个数组成员,一共有四个数组成员,所以需要运算4次。 +上面代码中,数组`a`和`b`的对应成员相加,结果放入数组`c`。它的运算模式是依次处理每个数组成员,一共有四个数组成员,所以需要运算 4 次。 如果采用 SIMD 模式,只要运算一次就够了。 @@ -30,7 +30,7 @@ var b = SIMD.Float32x4(5, 6, 7, 8); var c = SIMD.Float32x4.add(a, b); // Float32x4[6, 8, 10, 12] ``` -上面代码之中,数组`a`和`b`的四个成员的各自相加,只用一条指令就完成了。因此,速度比上一种写法提高了4倍。 +上面代码之中,数组`a`和`b`的四个成员的各自相加,只用一条指令就完成了。因此,速度比上一种写法提高了 4 倍。 一次 SIMD 运算,可以处理多个数据,这些数据被称为“通道”(lane)。上面代码中,一次运算了四个数据,因此就是四个通道。 @@ -41,34 +41,34 @@ v + w = 〈v1, …, vn〉+ 〈w1, …, wn〉 = 〈v1+w1, …, vn+wn〉 ``` -上面代码中,`v`和`w`是两个多元矢量。它们的加运算,在 SIMD 下是一个指令、而不是 n 个指令完成的,这就大大提高了效率。这对于3D动画、图像处理、信号处理、数值处理、加密等运算是非常重要的。比如,Canvas的`getImageData()`会将图像文件读成一个二进制数组,SIMD 就很适合对于这种数组的处理。 +上面代码中,`v`和`w`是两个多元矢量。它们的加运算,在 SIMD 下是一个指令、而不是 n 个指令完成的,这就大大提高了效率。这对于 3D 动画、图像处理、信号处理、数值处理、加密等运算是非常重要的。比如,Canvas 的`getImageData()`会将图像文件读成一个二进制数组,SIMD 就很适合对于这种数组的处理。 总的来说,SIMD 是数据并行处理(parallelism)的一种手段,可以加速一些运算密集型操作的速度。将来与 WebAssembly 结合以后,可以让 JavaScript 达到二进制代码的运行速度。 ## 数据类型 -SIMD 提供12种数据类型,总长度都是128个二进制位。 +SIMD 提供 12 种数据类型,总长度都是 128 个二进制位。 -- Float32x4:四个32位浮点数 -- Float64x2:两个64位浮点数 -- Int32x4:四个32位整数 -- Int16x8:八个16位整数 -- Int8x16:十六个8位整数 -- Uint32x4:四个无符号的32位整数 -- Uint16x8:八个无符号的16位整数 -- Uint8x16:十六个无符号的8位整数 -- Bool32x4:四个32位布尔值 -- Bool16x8:八个16位布尔值 -- Bool8x16:十六个8位布尔值 -- Bool64x2:两个64位布尔值 +- Float32x4:四个 32 位浮点数 +- Float64x2:两个 64 位浮点数 +- Int32x4:四个 32 位整数 +- Int16x8:八个 16 位整数 +- Int8x16:十六个 8 位整数 +- Uint32x4:四个无符号的 32 位整数 +- Uint16x8:八个无符号的 16 位整数 +- Uint8x16:十六个无符号的 8 位整数 +- Bool32x4:四个 32 位布尔值 +- Bool16x8:八个 16 位布尔值 +- Bool8x16:十六个 8 位布尔值 +- Bool64x2:两个 64 位布尔值 -每种数据类型被`x`符号分隔成两部分,后面的部分表示通道数,前面的部分表示每个通道的宽度和类型。比如,`Float32x4`就表示这个值有4个通道,每个通道是一个32位浮点数。 +每种数据类型被`x`符号分隔成两部分,后面的部分表示通道数,前面的部分表示每个通道的宽度和类型。比如,`Float32x4`就表示这个值有 4 个通道,每个通道是一个 32 位浮点数。 每个通道之中,可以放置四种数据。 -- 浮点数(float,比如1.0) +- 浮点数(float,比如 1.0) - 带符号的整数(Int,比如-1) -- 无符号的整数(Uint,比如1) +- 无符号的整数(Uint,比如 1) - 布尔值(Bool,包含`true`和`false`两种值) 每种 SIMD 的数据类型都是一个函数方法,可以传入参数,生成对应的值。 @@ -77,7 +77,7 @@ SIMD 提供12种数据类型,总长度都是128个二进制位。 var a = SIMD.Float32x4(1.0, 2.0, 3.0, 4.0); ``` -上面代码中,变量`a`就是一个128位、包含四个32位浮点数(即四个通道)的值。 +上面代码中,变量`a`就是一个 128 位、包含四个 32 位浮点数(即四个通道)的值。 注意,这些数据类型方法都不是构造函数,前面不能加`new`,否则会报错。 @@ -92,7 +92,7 @@ var v = new SIMD.Float32x4(0, 1, 2, 3); ### SIMD.%type%.abs(),SIMD.%type%.neg() -`abs`方法接受一个SIMD值作为参数,将它的每个通道都转成绝对值,作为一个新的SIMD值返回。 +`abs`方法接受一个 SIMD 值作为参数,将它的每个通道都转成绝对值,作为一个新的 SIMD 值返回。 ```javascript var a = SIMD.Float32x4(-1, -2, 0, NaN); @@ -100,7 +100,7 @@ SIMD.Float32x4.abs(a) // Float32x4[1, 2, 0, NaN] ``` -`neg`方法接受一个SIMD值作为参数,将它的每个通道都转成负值,作为一个新的SIMD值返回。 +`neg`方法接受一个 SIMD 值作为参数,将它的每个通道都转成负值,作为一个新的 SIMD 值返回。 ```javascript var a = SIMD.Float32x4(-1, -2, 3, 0); @@ -114,7 +114,7 @@ SIMD.Float64x2.neg(b) ### SIMD.%type%.add(),SIMD.%type%.addSaturate() -`add`方法接受两个SIMD值作为参数,将它们的每个通道相加,作为一个新的SIMD值返回。 +`add`方法接受两个 SIMD 值作为参数,将它们的每个通道相加,作为一个新的 SIMD 值返回。 ```javascript var a = SIMD.Float32x4(1.0, 2.0, 3.0, 4.0); @@ -122,7 +122,7 @@ var b = SIMD.Float32x4(5.0, 10.0, 15.0, 20.0); var c = SIMD.Float32x4.add(a, b); ``` -上面代码中,经过加法运算,新的SIMD值为`(6.0, 12.0, 18.0. 24.0)`。 +上面代码中,经过加法运算,新的 SIMD 值为`(6.0, 12.0, 18.0. 24.0)`。 `addSaturate`方法跟`add`方法的作用相同,都是两个通道相加,但是溢出的处理不一致。对于`add`方法,如果两个值相加发生溢出,溢出的二进制位会被丢弃; `addSaturate`方法则是返回该数据类型的最大值。 @@ -138,13 +138,13 @@ SIMD.Int16x8.addSaturate(c, d); // Int16x8[32766, 32767, 32767, 32767, 2, 2, 2, 2] ``` -上面代码中,`Uint16`的最大值是65535,`Int16`的最大值是32767。一旦发生溢出,就返回这两个值。 +上面代码中,`Uint16`的最大值是 65535,`Int16`的最大值是 32767。一旦发生溢出,就返回这两个值。 注意,`Uint32x4`和`Int32x4`这两种数据类型没有`addSaturate`方法。 ### SIMD.%type%.sub(),SIMD.%type%.subSaturate() -`sub`方法接受两个SIMD值作为参数,将它们的每个通道相减,作为一个新的SIMD值返回。 +`sub`方法接受两个 SIMD 值作为参数,将它们的每个通道相减,作为一个新的 SIMD 值返回。 ```javascript var a = SIMD.Float32x4(-1, -2, 3, 4); @@ -171,7 +171,7 @@ SIMD.Int16x8.subSaturate(c, d) ### SIMD.%type%.mul(),SIMD.%type%.div(),SIMD.%type%.sqrt() -`mul`方法接受两个SIMD值作为参数,将它们的每个通道相乘,作为一个新的SIMD值返回。 +`mul`方法接受两个 SIMD 值作为参数,将它们的每个通道相乘,作为一个新的 SIMD 值返回。 ```javascript var a = SIMD.Float32x4(-1, -2, 3, 4); @@ -180,7 +180,7 @@ SIMD.Float32x4.mul(a, b) // Float32x4[-3, -6, 9, 12] ``` -`div`方法接受两个SIMD值作为参数,将它们的每个通道相除,作为一个新的SIMD值返回。 +`div`方法接受两个 SIMD 值作为参数,将它们的每个通道相除,作为一个新的 SIMD 值返回。 ```javascript var a = SIMD.Float32x4(2, 2, 2, 2); @@ -189,7 +189,7 @@ SIMD.Float32x4.div(a, b) // Float32x4[0.5, 0.5, 0.5, 0.5] ``` -`sqrt`方法接受一个SIMD值作为参数,求出每个通道的平方根,作为一个新的SIMD值返回。 +`sqrt`方法接受一个 SIMD 值作为参数,求出每个通道的平方根,作为一个新的 SIMD 值返回。 ```javascript var b = SIMD.Float64x2(4, 8); @@ -199,7 +199,7 @@ SIMD.Float64x2.sqrt(b) ### SIMD.%FloatType%.reciprocalApproximation(),SIMD.%type%.reciprocalSqrtApproximation() -`reciprocalApproximation`方法接受一个SIMD值作为参数,求出每个通道的倒数(`1 / x`),作为一个新的SIMD值返回。 +`reciprocalApproximation`方法接受一个 SIMD 值作为参数,求出每个通道的倒数(`1 / x`),作为一个新的 SIMD 值返回。 ```javascript var a = SIMD.Float32x4(1, 2, 3, 4); @@ -207,7 +207,7 @@ SIMD.Float32x4.reciprocalApproximation(a); // Float32x4[1, 0.5, 0.3333333432674408, 0.25] ``` -`reciprocalSqrtApproximation`方法接受一个SIMD值作为参数,求出每个通道的平方根的倒数(`1 / (x^0.5)`),作为一个新的SIMD值返回。 +`reciprocalSqrtApproximation`方法接受一个 SIMD 值作为参数,求出每个通道的平方根的倒数(`1 / (x^0.5)`),作为一个新的 SIMD 值返回。 ```javascript var a = SIMD.Float32x4(1, 2, 3, 4); @@ -219,7 +219,7 @@ SIMD.Float32x4.reciprocalSqrtApproximation(a) ### SIMD.%IntegerType%.shiftLeftByScalar() -`shiftLeftByScalar`方法接受一个SIMD值作为参数,然后将每个通道的值左移指定的位数,作为一个新的SIMD值返回。 +`shiftLeftByScalar`方法接受一个 SIMD 值作为参数,然后将每个通道的值左移指定的位数,作为一个新的 SIMD 值返回。 ```javascript var a = SIMD.Int32x4(1, 2, 4, 8); @@ -239,7 +239,7 @@ var jx4 = SIMD.Int32x4.shiftLeftByScalar(ix4, 32); ### SIMD.%IntegerType%.shiftRightByScalar() -`shiftRightByScalar`方法接受一个SIMD值作为参数,然后将每个通道的值右移指定的位数,返回一个新的SIMD值。 +`shiftRightByScalar`方法接受一个 SIMD 值作为参数,然后将每个通道的值右移指定的位数,返回一个新的 SIMD 值。 ```javascript var a = SIMD.Int32x4(1, 2, 4, -8); @@ -255,7 +255,7 @@ SIMD.Uint32x4.shiftRightByScalar(a, 1); // Uint32x4[0, 1, 2, 2147483644] ``` -上面代码中,`-8`右移一位变成了`2147483644`,是因为对于32位无符号整数来说,`-8`的二进制形式是`11111111111111111111111111111000`,右移一位就变成了`01111111111111111111111111111100`,相当于`2147483644`。 +上面代码中,`-8`右移一位变成了`2147483644`,是因为对于 32 位无符号整数来说,`-8`的二进制形式是`11111111111111111111111111111000`,右移一位就变成了`01111111111111111111111111111100`,相当于`2147483644`。 注意,只有整数的数据类型才有这个方法。 @@ -263,7 +263,7 @@ SIMD.Uint32x4.shiftRightByScalar(a, 1); ### SIMD.%type%.check() -`check`方法用于检查一个值是否为当前类型的SIMD值。如果是的,就返回这个值,否则就报错。 +`check`方法用于检查一个值是否为当前类型的 SIMD 值。如果是的,就返回这个值,否则就报错。 ```javascript var a = SIMD.Float32x4(1, 2, 3, 9); @@ -278,14 +278,14 @@ SIMD.Int32x4.check('hello world') // 报错 ### SIMD.%type%.extractLane(),SIMD.%type%.replaceLane() -`extractLane`方法用于返回给定通道的值。它接受两个参数,分别是SIMD值和通道编号。 +`extractLane`方法用于返回给定通道的值。它接受两个参数,分别是 SIMD 值和通道编号。 ```javascript var t = SIMD.Float32x4(1, 2, 3, 4); SIMD.Float32x4.extractLane(t, 2) // 3 ``` -`replaceLane`方法用于替换指定通道的值,并返回一个新的SIMD值。它接受三个参数,分别是原来的SIMD值、通道编号和新的通道值。 +`replaceLane`方法用于替换指定通道的值,并返回一个新的 SIMD 值。它接受三个参数,分别是原来的 SIMD 值、通道编号和新的通道值。 ```javascript var t = SIMD.Float32x4(1, 2, 3, 4); @@ -295,7 +295,7 @@ SIMD.Float32x4.replaceLane(t, 2, 42) ### SIMD.%type%.load() -`load`方法用于从二进制数组读入数据,生成一个新的SIMD值。 +`load`方法用于从二进制数组读入数据,生成一个新的 SIMD 值。 ```javascript var a = new Int32Array([1,2,3,4,5,6,7,8]); @@ -307,7 +307,7 @@ SIMD.Int32x4.load(a, 2); // Int32x4[3, 4, 5, 6] ``` -`load`方法接受两个参数:一个二进制数组和开始读取的位置(从0开始)。如果位置不合法(比如`-1`或者超出二进制数组的大小),就会抛出一个错误。 +`load`方法接受两个参数:一个二进制数组和开始读取的位置(从 0 开始)。如果位置不合法(比如`-1`或者超出二进制数组的大小),就会抛出一个错误。 这个方法还有三个变种`load1()`、`load2()`、`load3()`,表示从指定位置开始,只加载一个通道、二个通道、三个通道的值。 @@ -330,7 +330,7 @@ SIMD.Int32x4.load3(a, 0); ### SIMD.%type%.store() -`store`方法用于将一个SIMD值,写入一个二进制数组。它接受三个参数,分别是二进制数组、开始写入的数组位置、SIMD值。它返回写入值以后的二进制数组。 +`store`方法用于将一个 SIMD 值,写入一个二进制数组。它接受三个参数,分别是二进制数组、开始写入的数组位置、SIMD 值。它返回写入值以后的二进制数组。 ```javascript var t1 = new Int32Array(8); @@ -344,7 +344,7 @@ SIMD.Int32x4.store(t2, 2, v2) // Int32Array[0, 0, 1, 2, 3, 4, 0, 0] ``` -上面代码中,`t1`是一个二进制数组,`v1`是一个SIMD值,只有四个通道。所以写入`t1`以后,只有前四个位置有值,后四个位置都是0。而`t2`是从2号位置开始写入,所以前两个位置和后两个位置都是0。 +上面代码中,`t1`是一个二进制数组,`v1`是一个 SIMD 值,只有四个通道。所以写入`t1`以后,只有前四个位置有值,后四个位置都是 0。而`t2`是从 2 号位置开始写入,所以前两个位置和后两个位置都是 0。 这个方法还有三个变种`store1()`、`store2()`和`store3()`,表示只写入一个通道、二个通道和三个通道的值。 @@ -357,7 +357,7 @@ SIMD.Int32x4.store1(tarray, 0, value); ### SIMD.%type%.splat() -`splat`方法返回一个新的SIMD值,该值的所有通道都会设成同一个预先给定的值。 +`splat`方法返回一个新的 SIMD 值,该值的所有通道都会设成同一个预先给定的值。 ```javascript SIMD.Float32x4.splat(3); @@ -366,11 +366,11 @@ SIMD.Float64x2.splat(3); // Float64x2[3, 3] ``` -如果省略参数,所有整数型的SIMD值都会设定`0`,浮点型的SIMD值都会设成`NaN`。 +如果省略参数,所有整数型的 SIMD 值都会设定`0`,浮点型的 SIMD 值都会设成`NaN`。 ### SIMD.%type%.swizzle() -`swizzle`方法返回一个新的SIMD值,重新排列原有的SIMD值的通道顺序。 +`swizzle`方法返回一个新的 SIMD 值,重新排列原有的 SIMD 值的通道顺序。 ```javascript var t = SIMD.Float32x4(1, 2, 3, 4); @@ -378,7 +378,7 @@ SIMD.Float32x4.swizzle(t, 1, 2, 0, 3); // Float32x4[2,3,1,4] ``` -上面代码中,`swizzle`方法的第一个参数是原有的SIMD值,后面的参数对应将要返回的SIMD值的四个通道。它的意思是新的SIMD的四个通道,依次是原来SIMD值的1号通道、2号通道、0号通道、3号通道。由于SIMD值最多可以有16个通道,所以`swizzle`方法除了第一个参数以外,最多还可以接受16个参数。 +上面代码中,`swizzle`方法的第一个参数是原有的 SIMD 值,后面的参数对应将要返回的 SIMD 值的四个通道。它的意思是新的 SIMD 的四个通道,依次是原来 SIMD 值的 1 号通道、2 号通道、0 号通道、3 号通道。由于 SIMD 值最多可以有 16 个通道,所以`swizzle`方法除了第一个参数以外,最多还可以接受 16 个参数。 下面是另一个例子。 @@ -398,7 +398,7 @@ var d = SIMD.Float32x4.swizzle(a, 3, 2, 1, 0); ### SIMD.%type%.shuffle() -`shuffle`方法从两个SIMD值之中取出指定通道,返回一个新的SIMD值。 +`shuffle`方法从两个 SIMD 值之中取出指定通道,返回一个新的 SIMD 值。 ```javascript var a = SIMD.Float32x4(1, 2, 3, 4); @@ -408,13 +408,13 @@ SIMD.Float32x4.shuffle(a, b, 1, 5, 7, 2); // Float32x4[2, 6, 8, 3] ``` -上面代码中,`a`和`b`一共有8个通道,依次编号为0到7。`shuffle`根据编号,取出相应的通道,返回一个新的SIMD值。 +上面代码中,`a`和`b`一共有 8 个通道,依次编号为 0 到 7。`shuffle`根据编号,取出相应的通道,返回一个新的 SIMD 值。 ## 静态方法:比较运算 ### SIMD.%type%.equal(),SIMD.%type%.notEqual() -`equal`方法用来比较两个SIMD值`a`和`b`的每一个通道,根据两者是否精确相等(`a === b`),得到一个布尔值。最后,所有通道的比较结果,组成一个新的SIMD值,作为掩码返回。`notEqual`方法则是比较两个通道是否不相等(`a !== b`)。 +`equal`方法用来比较两个 SIMD 值`a`和`b`的每一个通道,根据两者是否精确相等(`a === b`),得到一个布尔值。最后,所有通道的比较结果,组成一个新的 SIMD 值,作为掩码返回。`notEqual`方法则是比较两个通道是否不相等(`a !== b`)。 ```javascript var a = SIMD.Float32x4(1, 2, 3, 9); @@ -429,7 +429,7 @@ SIMD.Float32x4.notEqual(a,b); ### SIMD.%type%.greaterThan(),SIMD.%type%.greaterThanOrEqual() -`greatThan`方法用来比较两个SIMD值`a`和`b`的每一个通道,如果在该通道中,`a`较大就得到`true`,否则得到`false`。最后,所有通道的比较结果,组成一个新的SIMD值,作为掩码返回。`greaterThanOrEqual`则是比较`a`是否大于等于`b`。 +`greatThan`方法用来比较两个 SIMD 值`a`和`b`的每一个通道,如果在该通道中,`a`较大就得到`true`,否则得到`false`。最后,所有通道的比较结果,组成一个新的 SIMD 值,作为掩码返回。`greaterThanOrEqual`则是比较`a`是否大于等于`b`。 ```javascript var a = SIMD.Float32x4(1, 6, 3, 11); @@ -444,7 +444,7 @@ SIMD.Float32x4.greaterThanOrEqual(a, b) ### SIMD.%type%.lessThan(),SIMD.%type%.lessThanOrEqual() -`lessThan`方法用来比较两个SIMD值`a`和`b`的每一个通道,如果在该通道中,`a`较小就得到`true`,否则得到`false`。最后,所有通道的比较结果,会组成一个新的SIMD值,作为掩码返回。`lessThanOrEqual`方法则是比较`a`是否等于`b`。 +`lessThan`方法用来比较两个 SIMD 值`a`和`b`的每一个通道,如果在该通道中,`a`较小就得到`true`,否则得到`false`。最后,所有通道的比较结果,会组成一个新的 SIMD 值,作为掩码返回。`lessThanOrEqual`方法则是比较`a`是否等于`b`。 ```javascript var a = SIMD.Float32x4(1, 2, 3, 11); @@ -459,7 +459,7 @@ SIMD.Float32x4.lessThanOrEqual(a, b) ### SIMD.%type%.select() -`select`方法通过掩码生成一个新的SIMD值。它接受三个参数,分别是掩码和两个SIMD值。 +`select`方法通过掩码生成一个新的 SIMD 值。它接受三个参数,分别是掩码和两个 SIMD 值。 ```javascript var a = SIMD.Float32x4(1, 2, 3, 4); @@ -471,7 +471,7 @@ SIMD.Float32x4.select(mask, a, b); // Float32x4[1, 6, 7, 4] ``` -上面代码中,`select`方法接受掩码和两个SIMD值作为参数。当某个通道对应的掩码为`true`时,会选择第一个SIMD值的对应通道,否则选择第二个SIMD值的对应通道。 +上面代码中,`select`方法接受掩码和两个 SIMD 值作为参数。当某个通道对应的掩码为`true`时,会选择第一个 SIMD 值的对应通道,否则选择第二个 SIMD 值的对应通道。 这个方法通常与比较运算符结合使用。 @@ -486,11 +486,11 @@ var result = SIMD.Float32x4.select(mask, a, b); // Float32x4[0, 6, 3, 4] ``` -上面代码中,先通过`lessThan`方法生成一个掩码,然后通过`select`方法生成一个由每个通道的较小值组成的新的SIMD值。 +上面代码中,先通过`lessThan`方法生成一个掩码,然后通过`select`方法生成一个由每个通道的较小值组成的新的 SIMD 值。 ### SIMD.%BooleanType%.allTrue(),SIMD.%BooleanType%.anyTrue() -`allTrue`方法接受一个SIMD值作为参数,然后返回一个布尔值,表示该SIMD值的所有通道是否都为`true`。 +`allTrue`方法接受一个 SIMD 值作为参数,然后返回一个布尔值,表示该 SIMD 值的所有通道是否都为`true`。 ```javascript var a = SIMD.Bool32x4(true, true, true, true); @@ -524,7 +524,7 @@ var b2 = SIMD.Int32x4.anyTrue(ix4); // true ### SIMD.%type%.min(),SIMD.%type%.minNum() -`min`方法接受两个SIMD值作为参数,将两者的对应通道的较小值,组成一个新的SIMD值返回。 +`min`方法接受两个 SIMD 值作为参数,将两者的对应通道的较小值,组成一个新的 SIMD 值返回。 ```javascript var a = SIMD.Float32x4(-1, -2, 3, 5.2); @@ -555,7 +555,7 @@ var dx4 = SIMD.Float32x4.minNum(ax4, bx4); ### SIMD.%type%.max(),SIMD.%type%.maxNum() -`max`方法接受两个SIMD值作为参数,将两者的对应通道的较大值,组成一个新的SIMD值返回。 +`max`方法接受两个 SIMD 值作为参数,将两者的对应通道的较大值,组成一个新的 SIMD 值返回。 ```javascript var a = SIMD.Float32x4(-1, -2, 3, 5.2); @@ -586,7 +586,7 @@ SIMD.Float64x2.maxNum(c, d) ### SIMD.%type%.and(),SIMD.%type%.or(),SIMD.%type%.xor(),SIMD.%type%.not() -`and`方法接受两个SIMD值作为参数,返回两者对应的通道进行二进制`AND`运算(`&`)后得到的新的SIMD值。 +`and`方法接受两个 SIMD 值作为参数,返回两者对应的通道进行二进制`AND`运算(`&`)后得到的新的 SIMD 值。 ```javascript var a = SIMD.Int32x4(1, 2, 4, 8); @@ -597,7 +597,7 @@ SIMD.Int32x4.and(a, b) 上面代码中,以通道`0`为例,`1`的二进制形式是`0001`,`5`的二进制形式是`01001`,所以进行`AND`运算以后,得到`0001`。 -`or`方法接受两个SIMD值作为参数,返回两者对应的通道进行二进制`OR`运算(`|`)后得到的新的SIMD值。 +`or`方法接受两个 SIMD 值作为参数,返回两者对应的通道进行二进制`OR`运算(`|`)后得到的新的 SIMD 值。 ```javascript var a = SIMD.Int32x4(1, 2, 4, 8); @@ -606,7 +606,7 @@ SIMD.Int32x4.or(a, b) // Int32x4[5, 7, 5, 13] ``` -`xor`方法接受两个SIMD值作为参数,返回两者对应的通道进行二进制”异或“运算(`^`)后得到的新的SIMD值。 +`xor`方法接受两个 SIMD 值作为参数,返回两者对应的通道进行二进制”异或“运算(`^`)后得到的新的 SIMD 值。 ```javascript var a = SIMD.Int32x4(1, 2, 4, 8); @@ -615,7 +615,7 @@ SIMD.Int32x4.xor(a, b) // Int32x4[4, 7, 1, 13] ``` -`not`方法接受一个SIMD值作为参数,返回每个通道进行二进制”否“运算(`~`)后得到的新的SIMD值。 +`not`方法接受一个 SIMD 值作为参数,返回每个通道进行二进制”否“运算(`~`)后得到的新的 SIMD 值。 ```javascript var a = SIMD.Int32x4(1, 2, 4, 8); @@ -623,11 +623,11 @@ SIMD.Int32x4.not(a) // Int32x4[-2, -3, -5, -9] ``` -上面代码中,`1`的否运算之所以得到`-2`,是因为在计算机内部,负数采用”2的补码“这种形式进行表示。也就是说,整数`n`的负数形式`-n`,是对每一个二进制位取反以后,再加上1。因此,直接取反就相当于负数形式再减去1,比如`1`的负数形式是`-1`,再减去1,就得到了`-2`。 +上面代码中,`1`的否运算之所以得到`-2`,是因为在计算机内部,负数采用”2 的补码“这种形式进行表示。也就是说,整数`n`的负数形式`-n`,是对每一个二进制位取反以后,再加上 1。因此,直接取反就相当于负数形式再减去 1,比如`1`的负数形式是`-1`,再减去 1,就得到了`-2`。 ## 静态方法:数据类型转换 -SIMD提供以下方法,用来将一种数据类型转为另一种数据类型。 +SIMD 提供以下方法,用来将一种数据类型转为另一种数据类型。 - `SIMD.%type%.fromFloat32x4()` - `SIMD.%type%.fromFloat32x4Bits()` @@ -662,7 +662,7 @@ SIMD.Int16x8.fromFloat32x4Bits(t); // Int16x8[0, 16256, 0, 16384, 0, 16448, 0, 16512] ``` -上面代码中,原始SIMD值`t`是4通道的,而目标值是8通道的。 +上面代码中,原始 SIMD 值`t`是 4 通道的,而目标值是 8 通道的。 如果数据转换时,原通道的数据大小,超过了目标通道的最大宽度,就会报错。 @@ -670,7 +670,7 @@ SIMD.Int16x8.fromFloat32x4Bits(t); ### SIMD.%type%.prototype.toString() -`toString`方法返回一个SIMD值的字符串形式。 +`toString`方法返回一个 SIMD 值的字符串形式。 ```javascript var a = SIMD.Float32x4(11, 22, 33, 44); @@ -692,7 +692,7 @@ function average(list) { } ``` -使用SIMD,可以将计算次数减少到`n`次的四分之一。 +使用 SIMD,可以将计算次数减少到`n`次的四分之一。 ```javascript function average(list) { @@ -713,4 +713,3 @@ function average(list) { ``` 上面代码先是每隔四位,将所有的值读入一个 SIMD,然后立刻累加。然后,得到累加值四个通道的总和,再除以`n`就可以了。 - diff --git a/docs/spec.md b/docs/spec.md index aa7fee02d..d3a258fa8 100644 --- a/docs/spec.md +++ b/docs/spec.md @@ -6,17 +6,17 @@ 一般来说,没有必要阅读规格,除非你要写编译器。因为规格写得非常抽象和精炼,又缺乏实例,不容易理解,而且对于解决实际的应用问题,帮助不大。但是,如果你遇到疑难的语法问题,实在找不到答案,这时可以去查看规格文件,了解语言标准是怎么说的。规格是解决问题的“最后一招”。 -这对JavaScript语言很有必要。因为它的使用场景复杂,语法规则不统一,例外很多,各种运行环境的行为不一致,导致奇怪的语法问题层出不穷,任何语法书都不可能囊括所有情况。查看规格,不失为一种解决语法问题的最可靠、最权威的终极方法。 +这对 JavaScript 语言很有必要。因为它的使用场景复杂,语法规则不统一,例外很多,各种运行环境的行为不一致,导致奇怪的语法问题层出不穷,任何语法书都不可能囊括所有情况。查看规格,不失为一种解决语法问题的最可靠、最权威的终极方法。 -本章介绍如何读懂ECMAScript 6的规格文件。 +本章介绍如何读懂 ECMAScript 6 的规格文件。 -ECMAScript 6的规格,可以在ECMA国际标准组织的官方网站([www.ecma-international.org/ecma-262/6.0/](http://www.ecma-international.org/ecma-262/6.0/))免费下载和在线阅读。 +ECMAScript 6 的规格,可以在 ECMA 国际标准组织的官方网站([www.ecma-international.org/ecma-262/6.0/](http://www.ecma-international.org/ecma-262/6.0/))免费下载和在线阅读。 -这个规格文件相当庞大,一共有26章,A4打印的话,足足有545页。它的特点就是规定得非常细致,每一个语法行为、每一个函数的实现都做了详尽的清晰的描述。基本上,编译器作者只要把每一步翻译成代码就可以了。这很大程度上,保证了所有ES6实现都有一致的行为。 +这个规格文件相当庞大,一共有 26 章,A4 打印的话,足足有 545 页。它的特点就是规定得非常细致,每一个语法行为、每一个函数的实现都做了详尽的清晰的描述。基本上,编译器作者只要把每一步翻译成代码就可以了。这很大程度上,保证了所有 ES6 实现都有一致的行为。 -ECMAScript 6规格的26章之中,第1章到第3章是对文件本身的介绍,与语言关系不大。第4章是对这门语言总体设计的描述,有兴趣的读者可以读一下。第5章到第8章是语言宏观层面的描述。第5章是规格的名词解释和写法的介绍,第6章介绍数据类型,第7章介绍语言内部用到的抽象操作,第8章介绍代码如何运行。第9章到第26章介绍具体的语法。 +ECMAScript 6 规格的 26 章之中,第 1 章到第 3 章是对文件本身的介绍,与语言关系不大。第 4 章是对这门语言总体设计的描述,有兴趣的读者可以读一下。第 5 章到第 8 章是语言宏观层面的描述。第 5 章是规格的名词解释和写法的介绍,第 6 章介绍数据类型,第 7 章介绍语言内部用到的抽象操作,第 8 章介绍代码如何运行。第 9 章到第 26 章介绍具体的语法。 -对于一般用户来说,除了第4章,其他章节都涉及某一方面的细节,不用通读,只要在用到的时候,查阅相关章节即可。下面通过一些例子,介绍如何使用这份规格。 +对于一般用户来说,除了第 4 章,其他章节都涉及某一方面的细节,不用通读,只要在用到的时候,查阅相关章节即可。下面通过一些例子,介绍如何使用这份规格。 ## 相等运算符 @@ -28,7 +28,7 @@ ECMAScript 6规格的26章之中,第1章到第3章是对文件本身的介绍 0 == null ``` -如果你不确定答案,或者想知道语言内部怎么处理,就可以去查看规格,[7.2.12小节](http://www.ecma-international.org/ecma-262/6.0/#sec-7.2.12)是对相等运算符(`==`)的描述。 +如果你不确定答案,或者想知道语言内部怎么处理,就可以去查看规格,[7.2.12 小节](http://www.ecma-international.org/ecma-262/6.0/#sec-7.2.12)是对相等运算符(`==`)的描述。 规格对每一种语法行为的描述,都分成两部分:先是总体的行为描述,然后是实现的算法细节。相等运算符的总体描述,只有一句话。 @@ -40,23 +40,23 @@ ECMAScript 6规格的26章之中,第1章到第3章是对文件本身的介绍 > 1. ReturnIfAbrupt(x). > 1. ReturnIfAbrupt(y). -> 1. If `Type(x)` is the same as `Type(y)`, then -> Return the result of performing Strict Equality Comparison `x === y`. +> 1. If `Type(x)` is the same as `Type(y)`, then\ +> Return the result of performing Strict Equality Comparison `x === y`. > 1. If `x` is `null` and `y` is `undefined`, return `true`. > 1. If `x` is `undefined` and `y` is `null`, return `true`. -> 1. If `Type(x)` is Number and `Type(y)` is String, -> return the result of the comparison `x == ToNumber(y)`. -> 1. If `Type(x)` is String and `Type(y)` is Number, -> return the result of the comparison `ToNumber(x) == y`. +> 1. If `Type(x)` is Number and `Type(y)` is String,\ +> return the result of the comparison `x == ToNumber(y)`. +> 1. If `Type(x)` is String and `Type(y)` is Number,\ +> return the result of the comparison `ToNumber(x) == y`. > 1. If `Type(x)` is Boolean, return the result of the comparison `ToNumber(x) == y`. > 1. If `Type(y)` is Boolean, return the result of the comparison `x == ToNumber(y)`. -> 1. If `Type(x)` is either String, Number, or Symbol and `Type(y)` is Object, then -> return the result of the comparison `x == ToPrimitive(y)`. -> 1. If `Type(x)` is Object and `Type(y)` is either String, Number, or Symbol, then -> return the result of the comparison `ToPrimitive(x) == y`. +> 1. If `Type(x)` is either String, Number, or Symbol and `Type(y)` is Object, then\ +> return the result of the comparison `x == ToPrimitive(y)`. +> 1. If `Type(x)` is Object and `Type(y)` is either String, Number, or Symbol, then\ +> return the result of the comparison `ToPrimitive(x) == y`. > 1. Return `false`. -上面这段算法,一共有12步,翻译如下。 +上面这段算法,一共有 12 步,翻译如下。 > 1. 如果`x`不是正常值(比如抛出一个错误),中断执行。 > 1. 如果`y`不是正常值,中断执行。 @@ -71,7 +71,7 @@ ECMAScript 6规格的26章之中,第1章到第3章是对文件本身的介绍 > 1. 如果`Type(x)`是对象,`Type(y)`是字符串或数值或`Symbol`值,返回`ToPrimitive(x) == y`的结果。 > 1. 返回`false`。 -由于`0`的类型是数值,`null`的类型是Null(这是规格[4.3.13小节](http://www.ecma-international.org/ecma-262/6.0/#sec-4.3.13)的规定,是内部Type运算的结果,跟`typeof`运算符无关)。因此上面的前11步都得不到结果,要到第12步才能得到`false`。 +由于`0`的类型是数值,`null`的类型是 Null(这是规格[4.3.13 小节](http://www.ecma-international.org/ecma-262/6.0/#sec-4.3.13)的规定,是内部 Type 运算的结果,跟`typeof`运算符无关)。因此上面的前 11 步都得不到结果,要到第 12 步才能得到`false`。 ```javascript 0 == null // false @@ -94,7 +94,7 @@ a2[0] // undefined a1[0] === a2[0] // true ``` -上面代码中,数组`a1`的成员是三个`undefined`,数组`a2`的成员是三个空位。这两个数组很相似,长度都是3,每个位置的成员读取出来都是`undefined`。 +上面代码中,数组`a1`的成员是三个`undefined`,数组`a2`的成员是三个空位。这两个数组很相似,长度都是 3,每个位置的成员读取出来都是`undefined`。 但是,它们实际上存在重大差异。 @@ -116,23 +116,23 @@ a2.map(n => 1) // [, , ,] 为什么`a1`与`a2`成员的行为不一致?数组的成员是`undefined`或空位,到底有什么不同? -规格的[12.2.5小节《数组的初始化》](http://www.ecma-international.org/ecma-262/6.0/#sec-12.2.5)给出了答案。 +规格的[12.2.5 小节《数组的初始化》](http://www.ecma-international.org/ecma-262/6.0/#sec-12.2.5)给出了答案。 > “Array elements may be elided at the beginning, middle or end of the element list. Whenever a comma in the element list is not preceded by an AssignmentExpression (i.e., a comma at the beginning or after another comma), the missing array element contributes to the length of the Array and increases the index of subsequent elements. Elided array elements are not defined. If an element is elided at the end of an array, that element does not contribute to the length of the Array.” 翻译如下。 -> "数组成员可以省略。只要逗号前面没有任何表达式,数组的`length`属性就会加1,并且相应增加其后成员的位置索引。被省略的成员不会被定义。如果被省略的成员是数组最后一个成员,则不会导致数组`length`属性增加。” +> "数组成员可以省略。只要逗号前面没有任何表达式,数组的`length`属性就会加 1,并且相应增加其后成员的位置索引。被省略的成员不会被定义。如果被省略的成员是数组最后一个成员,则不会导致数组`length`属性增加。” -上面的规格说得很清楚,数组的空位会反映在`length`属性,也就是说空位有自己的位置,但是这个位置的值是未定义,即这个值是不存在的。如果一定要读取,结果就是`undefined`(因为`undefined`在JavaScript语言中表示不存在)。 +上面的规格说得很清楚,数组的空位会反映在`length`属性,也就是说空位有自己的位置,但是这个位置的值是未定义,即这个值是不存在的。如果一定要读取,结果就是`undefined`(因为`undefined`在 JavaScript 语言中表示不存在)。 -这就解释了为什么`in`运算符、数组的`hasOwnProperty`方法、`Object.keys`方法,都取不到空位的属性名。因为这个属性名根本就不存在,规格里面没说要为空位分配属性名(位置索引),只说要为下一个元素的位置索引加1。 +这就解释了为什么`in`运算符、数组的`hasOwnProperty`方法、`Object.keys`方法,都取不到空位的属性名。因为这个属性名根本就不存在,规格里面没说要为空位分配属性名(位置索引),只说要为下一个元素的位置索引加 1。 至于为什么数组的`map`方法会跳过空位,请看下一节。 -## 数组的map方法 +## 数组的 map 方法 -规格的[22.1.3.15小节](http://www.ecma-international.org/ecma-262/6.0/#sec-22.1.3.15)定义了数组的`map`方法。该小节先是总体描述`map`方法的行为,里面没有提到数组空位。 +规格的[22.1.3.15 小节](http://www.ecma-international.org/ecma-262/6.0/#sec-22.1.3.15)定义了数组的`map`方法。该小节先是总体描述`map`方法的行为,里面没有提到数组空位。 后面的算法描述是这样的。 @@ -145,18 +145,18 @@ a2.map(n => 1) // [, , ,] > 1. Let `A` be `ArraySpeciesCreate(O, len)`. > 1. `ReturnIfAbrupt(A)`. > 1. Let `k` be 0. -> 1. Repeat, while `k` < `len` -> a. Let `Pk` be `ToString(k)`. -> b. Let `kPresent` be `HasProperty(O, Pk)`. -> c. `ReturnIfAbrupt(kPresent)`. -> d. If `kPresent` is `true`, then -> d-1. Let `kValue` be `Get(O, Pk)`. -> d-2. `ReturnIfAbrupt(kValue)`. -> d-3. Let `mappedValue` be `Call(callbackfn, T, «kValue, k, O»)`. -> d-4. `ReturnIfAbrupt(mappedValue)`. -> d-5. Let `status` be `CreateDataPropertyOrThrow (A, Pk, mappedValue)`. -> d-6. `ReturnIfAbrupt(status)`. -> e. Increase `k` by 1. +> 1. Repeat, while `k` < `len`\ +> a. Let `Pk` be `ToString(k)`.\ +> b. Let `kPresent` be `HasProperty(O, Pk)`.\ +> c. `ReturnIfAbrupt(kPresent)`.\ +> d. If `kPresent` is `true`, then\ +> d-1. Let `kValue` be `Get(O, Pk)`.\ +> d-2. `ReturnIfAbrupt(kValue)`.\ +> d-3. Let `mappedValue` be `Call(callbackfn, T, «kValue, k, O»)`.\ +> d-4. `ReturnIfAbrupt(mappedValue)`.\ +> d-5. Let `status` be `CreateDataPropertyOrThrow (A, Pk, mappedValue)`.\ +> d-6. `ReturnIfAbrupt(status)`.\ +> e. Increase `k` by 1. > 1. Return `A`. 翻译如下。 @@ -165,26 +165,26 @@ a2.map(n => 1) // [, , ,] > 1. 如果报错就返回 > 1. 求出当前数组的`length`属性 > 1. 如果报错就返回 -> 1. 如果map方法的参数`callbackfn`不可执行,就报错 -> 1. 如果map方法的参数之中,指定了`this`,就让`T`等于该参数,否则`T`为`undefined` +> 1. 如果 map 方法的参数`callbackfn`不可执行,就报错 +> 1. 如果 map 方法的参数之中,指定了`this`,就让`T`等于该参数,否则`T`为`undefined` > 1. 生成一个新的数组`A`,跟当前数组的`length`属性保持一致 > 1. 如果报错就返回 -> 1. 设定`k`等于0 -> 1. 只要`k`小于当前数组的`length`属性,就重复下面步骤 -> a. 设定`Pk`等于`ToString(k)`,即将`K`转为字符串 -> b. 设定`kPresent`等于`HasProperty(O, Pk)`,即求当前数组有没有指定属性 -> c. 如果报错就返回 -> d. 如果`kPresent`等于`true`,则进行下面步骤 -> d-1. 设定`kValue`等于`Get(O, Pk)`,取出当前数组的指定属性 -> d-2. 如果报错就返回 -> d-3. 设定`mappedValue`等于`Call(callbackfn, T, «kValue, k, O»)`,即执行回调函数 -> d-4. 如果报错就返回 -> d-5. 设定`status`等于`CreateDataPropertyOrThrow (A, Pk, mappedValue)`,即将回调函数的值放入`A`数组的指定位置 -> d-6. 如果报错就返回 -> e. `k`增加1 +> 1. 设定`k`等于 0 +> 1. 只要`k`小于当前数组的`length`属性,就重复下面步骤\ +> a. 设定`Pk`等于`ToString(k)`,即将`K`转为字符串\ +> b. 设定`kPresent`等于`HasProperty(O, Pk)`,即求当前数组有没有指定属性\ +> c. 如果报错就返回\ +> d. 如果`kPresent`等于`true`,则进行下面步骤\ +> d-1. 设定`kValue`等于`Get(O, Pk)`,取出当前数组的指定属性\ +> d-2. 如果报错就返回\ +> d-3. 设定`mappedValue`等于`Call(callbackfn, T, «kValue, k, O»)`,即执行回调函数\ +> d-4. 如果报错就返回\ +> d-5. 设定`status`等于`CreateDataPropertyOrThrow (A, Pk, mappedValue)`,即将回调函数的值放入`A`数组的指定位置\ +> d-6. 如果报错就返回\ +> e. `k`增加 1 > 1. 返回`A` -仔细查看上面的算法,可以发现,当处理一个全是空位的数组时,前面步骤都没有问题。进入第10步的b时,`kpresent`会报错,因为空位对应的属性名,对于数组来说是不存在的,因此就会返回,不会进行后面的步骤。 +仔细查看上面的算法,可以发现,当处理一个全是空位的数组时,前面步骤都没有问题。进入第 10 步的 b 时,`kpresent`会报错,因为空位对应的属性名,对于数组来说是不存在的,因此就会返回,不会进行后面的步骤。 ```javascript const arr = [, , ,]; @@ -196,7 +196,7 @@ arr.map(n => { 上面代码中,`arr`是一个全是空位的数组,`map`方法遍历成员时,发现是空位,就直接跳过,不会进入回调函数。因此,回调函数里面的`console.log`语句根本不会执行,整个`map`方法返回一个全是空位的新数组。 -V8引擎对`map`方法的[实现](https://github.com/v8/v8/blob/44c44521ae11859478b42004f57ea93df52526ee/src/js/array.js#L1347)如下,可以看到跟规格的算法描述完全一致。 +V8 引擎对`map`方法的[实现](https://github.com/v8/v8/blob/44c44521ae11859478b42004f57ea93df52526ee/src/js/array.js#L1347)如下,可以看到跟规格的算法描述完全一致。 ```javascript function ArrayMap(f, receiver) { diff --git a/docs/string.md b/docs/string.md index 697eaca34..3686d33fc 100644 --- a/docs/string.md +++ b/docs/string.md @@ -21,7 +21,7 @@ JavaScript 允许采用`\uxxxx`形式表示一个字符,其中`xxxx`表示字 // " 7" ``` -上面代码表示,如果直接在`\u`后面跟上超过`0xFFFF`的数值(比如`\u20BB7`),JavaScript会理解成`\u20BB+7`。由于`\u20BB`是一个不可打印字符,所以只会显示一个空格,后面跟着一个`7`。 +上面代码表示,如果直接在`\u`后面跟上超过`0xFFFF`的数值(比如`\u20BB7`),JavaScript 会理解成`\u20BB+7`。由于`\u20BB`是一个不可打印字符,所以只会显示一个空格,后面跟着一个`7`。 ES6 对这一点做出了改进,只要将码点放入大括号,就能正确解读该字符。 @@ -41,7 +41,7 @@ hell\u{6F} // 123 上面代码中,最后一个例子表明,大括号表示法与四字节的 UTF-16 编码是等价的。 -有了这种表示法之后,JavaScript 共有6种方法可以表示一个字符。 +有了这种表示法之后,JavaScript 共有 6 种方法可以表示一个字符。 ```javascript '\z' === 'z' // true @@ -53,7 +53,7 @@ hell\u{6F} // 123 ## codePointAt() -JavaScript内部,字符以UTF-16的格式储存,每个字符固定为`2`个字节。对于那些需要`4`个字节储存的字符(Unicode码点大于`0xFFFF`的字符),JavaScript会认为它们是两个字符。 +JavaScript 内部,字符以 UTF-16 的格式储存,每个字符固定为`2`个字节。对于那些需要`4`个字节储存的字符(Unicode 码点大于`0xFFFF`的字符),JavaScript 会认为它们是两个字符。 ```javascript var s = "𠮷"; @@ -65,9 +65,9 @@ s.charCodeAt(0) // 55362 s.charCodeAt(1) // 57271 ``` -上面代码中,汉字“𠮷”(注意,这个字不是“吉祥”的“吉”)的码点是`0x20BB7`,UTF-16编码为`0xD842 0xDFB7`(十进制为`55362 57271`),需要`4`个字节储存。对于这种`4`个字节的字符,JavaScript不能正确处理,字符串长度会误判为`2`,而且`charAt`方法无法读取整个字符,`charCodeAt`方法只能分别返回前两个字节和后两个字节的值。 +上面代码中,汉字“𠮷”(注意,这个字不是“吉祥”的“吉”)的码点是`0x20BB7`,UTF-16 编码为`0xD842 0xDFB7`(十进制为`55362 57271`),需要`4`个字节储存。对于这种`4`个字节的字符,JavaScript 不能正确处理,字符串长度会误判为`2`,而且`charAt`方法无法读取整个字符,`charCodeAt`方法只能分别返回前两个字节和后两个字节的值。 -ES6提供了`codePointAt`方法,能够正确处理4个字节储存的字符,返回一个字符的码点。 +ES6 提供了`codePointAt`方法,能够正确处理 4 个字节储存的字符,返回一个字符的码点。 ```javascript let s = '𠮷a'; @@ -78,9 +78,9 @@ s.codePointAt(1) // 57271 s.codePointAt(2) // 97 ``` -`codePointAt`方法的参数,是字符在字符串中的位置(从0开始)。上面代码中,JavaScript将“𠮷a”视为三个字符,codePointAt方法在第一个字符上,正确地识别了“𠮷”,返回了它的十进制码点134071(即十六进制的`20BB7`)。在第二个字符(即“𠮷”的后两个字节)和第三个字符“a”上,`codePointAt`方法的结果与`charCodeAt`方法相同。 +`codePointAt`方法的参数,是字符在字符串中的位置(从 0 开始)。上面代码中,JavaScript 将“𠮷a”视为三个字符,codePointAt 方法在第一个字符上,正确地识别了“𠮷”,返回了它的十进制码点 134071(即十六进制的`20BB7`)。在第二个字符(即“𠮷”的后两个字节)和第三个字符“a”上,`codePointAt`方法的结果与`charCodeAt`方法相同。 -总之,`codePointAt`方法会正确返回32位的UTF-16字符的码点。对于那些两个字节储存的常规字符,它的返回结果与`charCodeAt`方法相同。 +总之,`codePointAt`方法会正确返回 32 位的 UTF-16 字符的码点。对于那些两个字节储存的常规字符,它的返回结果与`charCodeAt`方法相同。 `codePointAt`方法返回的是码点的十进制值,如果想要十六进制的值,可以使用`toString`方法转换一下。 @@ -91,7 +91,7 @@ s.codePointAt(0).toString(16) // "20bb7" s.codePointAt(2).toString(16) // "61" ``` -你可能注意到了,`codePointAt`方法的参数,仍然是不正确的。比如,上面代码中,字符`a`在字符串`s`的正确位置序号应该是1,但是必须向`codePointAt`方法传入2。解决这个问题的一个办法是使用`for...of`循环,因为它会正确识别32位的UTF-16字符。 +你可能注意到了,`codePointAt`方法的参数,仍然是不正确的。比如,上面代码中,字符`a`在字符串`s`的正确位置序号应该是 1,但是必须向`codePointAt`方法传入 2。解决这个问题的一个办法是使用`for...of`循环,因为它会正确识别 32 位的 UTF-16 字符。 ```javascript let s = '𠮷a'; @@ -115,7 +115,7 @@ is32Bit("a") // false ## String.fromCodePoint() -ES5提供`String.fromCharCode`方法,用于从码点返回对应字符,但是这个方法不能识别32位的UTF-16字符(Unicode编号大于`0xFFFF`)。 +ES5 提供`String.fromCharCode`方法,用于从码点返回对应字符,但是这个方法不能识别 32 位的 UTF-16 字符(Unicode 编号大于`0xFFFF`)。 ```javascript String.fromCharCode(0x20BB7) @@ -124,7 +124,7 @@ String.fromCharCode(0x20BB7) 上面代码中,`String.fromCharCode`不能识别大于`0xFFFF`的码点,所以`0x20BB7`就发生了溢出,最高位`2`被舍弃了,最后返回码点`U+0BB7`对应的字符,而不是码点`U+20BB7`对应的字符。 -ES6提供了`String.fromCodePoint`方法,可以识别大于`0xFFFF`的字符,弥补了`String.fromCharCode`方法的不足。在作用上,正好与`codePointAt`方法相反。 +ES6 提供了`String.fromCodePoint`方法,可以识别大于`0xFFFF`的字符,弥补了`String.fromCharCode`方法的不足。在作用上,正好与`codePointAt`方法相反。 ```javascript String.fromCodePoint(0x20BB7) @@ -139,7 +139,7 @@ String.fromCodePoint(0x78, 0x1f680, 0x79) === 'x\uD83D\uDE80y' ## 字符串的遍历器接口 -ES6为字符串添加了遍历器接口(详见《Iterator》一章),使得字符串可以被`for...of`循环遍历。 +ES6 为字符串添加了遍历器接口(详见《Iterator》一章),使得字符串可以被`for...of`循环遍历。 ```javascript for (let codePoint of 'foo') { @@ -178,7 +178,7 @@ ES5 对字符串对象提供`charAt`方法,返回字符串给定位置的字 '𠮷'.charAt(0) // "\uD842" ``` -上面代码中,`charAt`方法返回的是UTF-16编码的第一个字节,实际上是无法显示的。 +上面代码中,`charAt`方法返回的是 UTF-16 编码的第一个字节,实际上是无法显示的。 目前,有一个提案,提出字符串实例的`at`方法,可以识别 Unicode 编号大于`0xFFFF`的字符,返回正确的字符。 @@ -225,11 +225,11 @@ ES6 提供字符串实例的`normalize()`方法,用来将字符的不同表示 上面代码表示,`NFC`参数返回字符的合成形式,`NFD`参数返回字符的分解形式。 -不过,`normalize`方法目前不能识别三个或三个以上字符的合成。这种情况下,还是只能使用正则表达式,通过Unicode编号区间判断。 +不过,`normalize`方法目前不能识别三个或三个以上字符的合成。这种情况下,还是只能使用正则表达式,通过 Unicode 编号区间判断。 ## includes(), startsWith(), endsWith() -传统上,JavaScript只有`indexOf`方法,可以用来确定一个字符串是否包含在另一个字符串中。ES6又提供了三种新方法。 +传统上,JavaScript 只有`indexOf`方法,可以用来确定一个字符串是否包含在另一个字符串中。ES6 又提供了三种新方法。 - **includes()**:返回布尔值,表示是否找到了参数字符串。 - **startsWith()**:返回布尔值,表示参数字符串是否在原字符串的头部。 @@ -280,13 +280,13 @@ s.includes('Hello', 6) // false // RangeError ``` -但是,如果参数是0到-1之间的小数,则等同于0,这是因为会先进行取整运算。0到-1之间的小数,取整以后等于`-0`,`repeat`视同为0。 +但是,如果参数是 0 到-1 之间的小数,则等同于 0,这是因为会先进行取整运算。0 到-1 之间的小数,取整以后等于`-0`,`repeat`视同为 0。 ```javascript 'na'.repeat(-0.9) // "" ``` -参数`NaN`等同于0。 +参数`NaN`等同于 0。 ```javascript 'na'.repeat(NaN) // "" @@ -334,7 +334,7 @@ ES2017 引入了字符串补全长度的功能。如果某个字符串不够指 'x'.padEnd(4) // 'x ' ``` -`padStart`的常见用途是为数值补全指定位数。下面代码生成10位的数值字符串。 +`padStart`的常见用途是为数值补全指定位数。下面代码生成 10 位的数值字符串。 ```javascript '1'.padStart(10, '0') // "0000000001" @@ -351,7 +351,7 @@ ES2017 引入了字符串补全长度的功能。如果某个字符串不够指 ## 模板字符串 -传统的JavaScript语言,输出模板通常是这样写的。 +传统的 JavaScript 语言,输出模板通常是这样写的。 ```javascript $('#result').append( @@ -362,7 +362,7 @@ $('#result').append( ); ``` -上面这种写法相当繁琐不方便,ES6引入了模板字符串解决这个问题。 +上面这种写法相当繁琐不方便,ES6 引入了模板字符串解决这个问题。 ```javascript $('#result').append(` @@ -409,7 +409,6 @@ $('#list').html(` 上面代码中,所有模板字符串的空格和换行,都是被保留的,比如`
    `标签前面会有一个换行。如果你不想要这个换行,可以使用`trim`方法消除它。 - ```javascript $('#list').html(`
      @@ -436,7 +435,7 @@ function authorize(user, action) { } ``` -大括号内部可以放入任意的JavaScript表达式,可以进行运算,以及引用对象属性。 +大括号内部可以放入任意的 JavaScript 表达式,可以进行运算,以及引用对象属性。 ```javascript let x = 1; @@ -474,7 +473,7 @@ let msg = `Hello, ${place}`; // 报错 ``` -由于模板字符串的大括号内部,就是执行JavaScript代码,因此如果大括号内部是一个字符串,将会原样输出。 +由于模板字符串的大括号内部,就是执行 JavaScript 代码,因此如果大括号内部是一个字符串,将会原样输出。 ```javascript `Hello ${'World'}` @@ -542,11 +541,11 @@ let template = ` `; ``` -上面代码在模板字符串之中,放置了一个常规模板。该模板使用`<%...%>`放置JavaScript代码,使用`<%= ... %>`输出JavaScript表达式。 +上面代码在模板字符串之中,放置了一个常规模板。该模板使用`<%...%>`放置 JavaScript 代码,使用`<%= ... %>`输出 JavaScript 表达式。 怎么编译这个模板字符串呢? -一种思路是将其转换为JavaScript表达式字符串。 +一种思路是将其转换为 JavaScript 表达式字符串。 ```javascript echo('
        '); @@ -785,7 +784,6 @@ message //

        <script>alert("abc")</script> has sent you a message.

        ``` - 标签模板的另一个应用,就是多语言转换(国际化处理)。 ```javascript @@ -793,7 +791,7 @@ i18n`Welcome to ${siteName}, you are visitor number ${visitorNumber}!` // "欢迎访问xxx,您是第xxxx位访问者!" ``` -模板字符串本身并不能取代Mustache之类的模板库,因为没有条件判断和循环处理功能,但是通过标签函数,你可以自己添加这些功能。 +模板字符串本身并不能取代 Mustache 之类的模板库,因为没有条件判断和循环处理功能,但是通过标签函数,你可以自己添加这些功能。 ```javascript // 下面的hashTemplate函数 @@ -807,7 +805,7 @@ let libraryHtml = hashTemplate` `; ``` -除此之外,你甚至可以使用标签模板,在JavaScript语言之中嵌入其他语言。 +除此之外,你甚至可以使用标签模板,在 JavaScript 语言之中嵌入其他语言。 ```javascript jsx` @@ -821,9 +819,9 @@ jsx` ` ``` -上面的代码通过`jsx`函数,将一个DOM字符串转为React对象。你可以在Github找到`jsx`函数的[具体实现](https://gist.github.com/lygaret/a68220defa69174bdec5)。 +上面的代码通过`jsx`函数,将一个 DOM 字符串转为 React 对象。你可以在 Github 找到`jsx`函数的[具体实现](https://gist.github.com/lygaret/a68220defa69174bdec5)。 -下面则是一个假想的例子,通过`java`函数,在JavaScript代码之中运行Java代码。 +下面则是一个假想的例子,通过`java`函数,在 JavaScript 代码之中运行 Java 代码。 ```javascript java` @@ -857,11 +855,11 @@ function tag(strings) { } ``` -上面代码中,`tag`函数的第一个参数`strings`,有一个`raw`属性,也指向一个数组。该数组的成员与`strings`数组完全一致。比如,`strings`数组是`["First line\nSecond line"]`,那么`strings.raw`数组就是`["First line\\nSecond line"]`。两者唯一的区别,就是字符串里面的斜杠都被转义了。比如,strings.raw数组会将`\n`视为`\\`和`n`两个字符,而不是换行符。这是为了方便取得转义之前的原始模板而设计的。 +上面代码中,`tag`函数的第一个参数`strings`,有一个`raw`属性,也指向一个数组。该数组的成员与`strings`数组完全一致。比如,`strings`数组是`["First line\nSecond line"]`,那么`strings.raw`数组就是`["First line\\nSecond line"]`。两者唯一的区别,就是字符串里面的斜杠都被转义了。比如,strings.raw 数组会将`\n`视为`\\`和`n`两个字符,而不是换行符。这是为了方便取得转义之前的原始模板而设计的。 ## String.raw() -ES6还为原生的String对象,提供了一个`raw`方法。 +ES6 还为原生的 String 对象,提供了一个`raw`方法。 `String.raw`方法,往往用来充当模板字符串的处理函数,返回一个斜杠都被转义(即斜杠前面再加一个斜杠)的字符串,对应于替换变量后的模板字符串。 @@ -941,11 +939,10 @@ function tag(strs) { tag`\unicode and \u{55}` ``` -上面代码中,模板字符串原本是应该报错的,但是由于放松了对字符串转义的限制,所以不报错了,JavaScript引擎将第一个字符设置为`undefined`,但是`raw`属性依然可以得到原始字符串,因此`tag`函数还是可以对原字符串进行处理。 +上面代码中,模板字符串原本是应该报错的,但是由于放松了对字符串转义的限制,所以不报错了,JavaScript 引擎将第一个字符设置为`undefined`,但是`raw`属性依然可以得到原始字符串,因此`tag`函数还是可以对原字符串进行处理。 注意,这种对字符串转义的放松,只在标签模板解析字符串时生效,不是标签模板的场合,依然会报错。 ```javascript let bad = `bad escape sequence: \unicode`; // 报错 ``` - diff --git a/docs/style.md b/docs/style.md index 52625a3cf..375377c5a 100644 --- a/docs/style.md +++ b/docs/style.md @@ -234,7 +234,7 @@ for (i = 0; i < len; i++) { const itemsCopy = [...items]; ``` -使用Array.from方法,将类似数组的对象转为数组。 +使用 Array.from 方法,将类似数组的对象转为数组。 ```javascript const foo = document.querySelectorAll('.foo'); @@ -251,7 +251,7 @@ const nodes = Array.from(foo); })(); ``` -那些需要使用函数表达式的场合,尽量用箭头函数代替。因为这样更简洁,而且绑定了this。 +那些需要使用函数表达式的场合,尽量用箭头函数代替。因为这样更简洁,而且绑定了 this。 ```javascript // bad @@ -268,7 +268,7 @@ const nodes = Array.from(foo); [1, 2, 3].map(x => x * x); ``` -箭头函数取代`Function.prototype.bind`,不应再用self/\_this/that绑定 this。 +箭头函数取代`Function.prototype.bind`,不应再用 self/\_this/that 绑定 this。 ```javascript // bad @@ -298,7 +298,7 @@ function divide(a, b, { option = false } = {}) { } ``` -不要在函数体内使用arguments变量,使用rest运算符(...)代替。因为rest运算符显式表明你想要获取参数,而且arguments是一个类似数组的对象,而rest运算符可以提供一个真正的数组。 +不要在函数体内使用 arguments 变量,使用 rest 运算符(...)代替。因为 rest 运算符显式表明你想要获取参数,而且 arguments 是一个类似数组的对象,而 rest 运算符可以提供一个真正的数组。 ```javascript // bad @@ -327,9 +327,9 @@ function handleThings(opts = {}) { } ``` -## Map结构 +## Map 结构 -注意区分Object和Map,只有模拟现实世界的实体对象时,才使用Object。如果只是需要`key: value`的数据结构,使用Map结构。因为Map有内建的遍历机制。 +注意区分 Object 和 Map,只有模拟现实世界的实体对象时,才使用 Object。如果只是需要`key: value`的数据结构,使用 Map 结构。因为 Map 有内建的遍历机制。 ```javascript let map = new Map(arr); @@ -349,7 +349,7 @@ for (let item of map.entries()) { ## Class -总是用Class,取代需要prototype的操作。因为Class的写法更简洁,更易于理解。 +总是用 Class,取代需要 prototype 的操作。因为 Class 的写法更简洁,更易于理解。 ```javascript // bad @@ -398,7 +398,7 @@ class PeekableQueue extends Queue { ## 模块 -首先,Module语法是JavaScript模块的标准写法,坚持使用这种写法。使用`import`取代`require`。 +首先,Module 语法是 JavaScript 模块的标准写法,坚持使用这种写法。使用`import`取代`require`。 ```javascript // bad @@ -468,7 +468,7 @@ const StyleGuide = { export default StyleGuide; ``` -## ESLint的使用 +## ESLint 的使用 ESLint 是一个语法规则和代码风格的检查工具,可以用来保证写出语法正确、风格统一的代码。 @@ -485,7 +485,7 @@ $ npm i -g eslint-config-airbnb $ npm i -g eslint-plugin-import eslint-plugin-jsx-a11y eslint-plugin-react ``` -最后,在项目的根目录下新建一个`.eslintrc`文件,配置ESLint。 +最后,在项目的根目录下新建一个`.eslintrc`文件,配置 ESLint。 ```javascript { @@ -522,4 +522,4 @@ index.js ✖ 5 problems (5 errors, 0 warnings) ``` -上面代码说明,原文件有五个错误,其中两个是不应该使用`var`命令,而要使用`let`或`const`;一个是定义了变量,却没有使用;另外两个是行首缩进为4个空格,而不是规定的2个空格。 +上面代码说明,原文件有五个错误,其中两个是不应该使用`var`命令,而要使用`let`或`const`;一个是定义了变量,却没有使用;另外两个是行首缩进为 4 个空格,而不是规定的 2 个空格。 diff --git a/docs/symbol.md b/docs/symbol.md index 7e2b35154..11c5384c8 100644 --- a/docs/symbol.md +++ b/docs/symbol.md @@ -247,7 +247,7 @@ const shapeType = { }; ``` -上面代码中,除了将`shapeType.triangle`的值设为一个Symbol,其他地方都不用修改。 +上面代码中,除了将`shapeType.triangle`的值设为一个 Symbol,其他地方都不用修改。 ## 属性名的遍历 @@ -293,7 +293,7 @@ Object.getOwnPropertySymbols(obj) 上面代码中,使用`Object.getOwnPropertyNames`方法得不到`Symbol`属性名,需要使用`Object.getOwnPropertySymbols`方法。 -另一个新的API,`Reflect.ownKeys`方法可以返回所有类型的键名,包括常规键名和 Symbol 键名。 +另一个新的 API,`Reflect.ownKeys`方法可以返回所有类型的键名,包括常规键名和 Symbol 键名。 ```javascript let obj = { @@ -341,7 +341,7 @@ Object.getOwnPropertySymbols(x) // [Symbol(size)] ## Symbol.for(),Symbol.keyFor() -有时,我们希望重新使用同一个 Symbol 值,`Symbol.for`方法可以做到这一点。它接受一个字符串作为参数,然后搜索有没有以该参数作为名称的Symbol值。如果有,就返回这个 Symbol 值,否则就新建并返回一个以该字符串为名称的 Symbol 值。 +有时,我们希望重新使用同一个 Symbol 值,`Symbol.for`方法可以做到这一点。它接受一个字符串作为参数,然后搜索有没有以该参数作为名称的 Symbol 值。如果有,就返回这个 Symbol 值,否则就新建并返回一个以该字符串为名称的 Symbol 值。 ```javascript let s1 = Symbol.for('foo'); @@ -352,7 +352,7 @@ s1 === s2 // true 上面代码中,`s1`和`s2`都是 Symbol 值,但是它们都是同样参数的`Symbol.for`方法生成的,所以实际上是同一个值。 -`Symbol.for()`与`Symbol()`这两种写法,都会生成新的 Symbol。它们的区别是,前者会被登记在全局环境中供搜索,后者不会。`Symbol.for()`不会每次调用就返回一个新的 Symbol 类型的值,而是会先检查给定的`key`是否已经存在,如果不存在才会新建一个值。比如,如果你调用`Symbol.for("cat")`30次,每次都会返回同一个 Symbol 值,但是调用`Symbol("cat")`30次,会返回30个不同的 Symbol 值。 +`Symbol.for()`与`Symbol()`这两种写法,都会生成新的 Symbol。它们的区别是,前者会被登记在全局环境中供搜索,后者不会。`Symbol.for()`不会每次调用就返回一个新的 Symbol 类型的值,而是会先检查给定的`key`是否已经存在,如果不存在才会新建一个值。比如,如果你调用`Symbol.for("cat")`30 次,每次都会返回同一个 Symbol 值,但是调用`Symbol("cat")`30 次,会返回 30 个不同的 Symbol 值。 ```javascript Symbol.for("bar") === Symbol.for("bar") @@ -391,9 +391,9 @@ iframe.contentWindow.Symbol.for('foo') === Symbol.for('foo') ## 实例:模块的 Singleton 模式 -Singleton模式指的是调用一个类,任何时候返回的都是同一个实例。 +Singleton 模式指的是调用一个类,任何时候返回的都是同一个实例。 -对于Node来说,模块文件可以看成是一个类。怎么保证每次执行这个模块文件,返回的都是同一个实例呢? +对于 Node 来说,模块文件可以看成是一个类。怎么保证每次执行这个模块文件,返回的都是同一个实例呢? 很容易想到,可以把实例放到顶层对象`global`。 @@ -428,7 +428,7 @@ global._foo = 123; 上面的代码,会使得别的脚本加载`mod.js`都失真。 -为了防止这种情况出现,我们就可以使用Symbol。 +为了防止这种情况出现,我们就可以使用 Symbol。 ```javascript // mod.js @@ -461,11 +461,11 @@ const FOO_KEY = Symbol('foo'); // 后面代码相同 …… ``` -上面代码将导致其他脚本都无法引用`FOO_KEY`。但这样也有一个问题,就是如果多次执行这个脚本,每次得到的`FOO_KEY`都是不一样的。虽然Node会将脚本的执行结果缓存,一般情况下,不会多次执行同一个脚本,但是用户可以手动清除缓存,所以也不是完全可靠。 +上面代码将导致其他脚本都无法引用`FOO_KEY`。但这样也有一个问题,就是如果多次执行这个脚本,每次得到的`FOO_KEY`都是不一样的。虽然 Node 会将脚本的执行结果缓存,一般情况下,不会多次执行同一个脚本,但是用户可以手动清除缓存,所以也不是完全可靠。 -## 内置的Symbol值 +## 内置的 Symbol 值 -除了定义自己使用的Symbol值以外,ES6还提供了11个内置的Symbol值,指向语言内部使用的方法。 +除了定义自己使用的 Symbol 值以外,ES6 还提供了 11 个内置的 Symbol 值,指向语言内部使用的方法。 ### Symbol.hasInstance @@ -712,7 +712,7 @@ myIterable[Symbol.iterator] = function* () { [...myIterable] // [1, 2, 3] ``` -对象进行`for...of`循环时,会调用`Symbol.iterator`方法,返回该对象的默认遍历器,详细介绍参见《Iterator和for...of循环》一章。 +对象进行`for...of`循环时,会调用`Symbol.iterator`方法,返回该对象的默认遍历器,详细介绍参见《Iterator 和 for...of 循环》一章。 ```javascript class Collection { @@ -787,11 +787,11 @@ let x = new Collection(); Object.prototype.toString.call(x) // "[object xxx]" ``` -ES6新增内置对象的`Symbol.toStringTag`属性值如下。 +ES6 新增内置对象的`Symbol.toStringTag`属性值如下。 - `JSON[Symbol.toStringTag]`:'JSON' - `Math[Symbol.toStringTag]`:'Math' -- Module对象`M[Symbol.toStringTag]`:'Module' +- Module 对象`M[Symbol.toStringTag]`:'Module' - `ArrayBuffer.prototype[Symbol.toStringTag]`:'ArrayBuffer' - `DataView.prototype[Symbol.toStringTag]`:'DataView' - `Map.prototype[Symbol.toStringTag]`:'Map' @@ -827,7 +827,7 @@ Object.keys(Array.prototype[Symbol.unscopables]) // ['copyWithin', 'entries', 'fill', 'find', 'findIndex', 'includes', 'keys'] ``` -上面代码说明,数组有7个属性,会被`with`命令排除。 +上面代码说明,数组有 7 个属性,会被`with`命令排除。 ```javascript // 没有 unscopables 时 From 5bfd96bd8722e7d911dd024693fdcc796c8ebdd3 Mon Sep 17 00:00:00 2001 From: Jing Ma Date: Wed, 15 Nov 2017 11:05:48 +0800 Subject: [PATCH 029/595] Fixed minor typo --- docs/array.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/array.md b/docs/array.md index d06756fab..3adaaed55 100644 --- a/docs/array.md +++ b/docs/array.md @@ -583,7 +583,7 @@ i32a.copyWithin(0, 2); 这两个方法都可以接受第二个参数,用来绑定回调函数的`this`对象。 -另外,这两个方法都可以发现`NaN`,弥补了数组的`IndexOf`方法的不足。 +另外,这两个方法都可以发现`NaN`,弥补了数组的`indexOf`方法的不足。 ```javascript [NaN].indexOf(NaN) From dad929d01e9d04773fa7ca88a39c78d7bcf5b6d3 Mon Sep 17 00:00:00 2001 From: ruanyf Date: Tue, 21 Nov 2017 13:33:34 +0800 Subject: [PATCH 030/595] docs(decorator): edit decorator/publishEvent --- docs/decorator.md | 42 +++++++++++++++++++++++++++++------------- 1 file changed, 29 insertions(+), 13 deletions(-) diff --git a/docs/decorator.md b/docs/decorator.md index c32e21fe6..0e3e40cd5 100644 --- a/docs/decorator.md +++ b/docs/decorator.md @@ -476,15 +476,23 @@ person.facepalmWithoutWarning(); 我们可以使用修饰器,使得对象的方法被调用时,自动发出一个事件。 ```javascript -import postal from "postal/lib/postal.lodash"; +const postal = require("postal/lib/postal.lodash"); export default function publish(topic, channel) { + const channelName = channel || '/'; + const msgChannel = postal.channel(channelName); + msgChannel.subscribe(topic, v => { + console.log('频道: ', channelName); + console.log('事件: ', topic); + console.log('数据: ', v); + }); + return function(target, name, descriptor) { const fn = descriptor.value; descriptor.value = function() { let value = fn.apply(this, arguments); - postal.channel(channel || target.channel || "/").publish(topic, value); + msgChannel.publish(topic, value); }; }; } @@ -495,29 +503,37 @@ export default function publish(topic, channel) { 它的用法如下。 ```javascript -import publish from "path/to/decorators/publish"; +// index.js +import publish from './publish'; class FooComponent { - @publish("foo.some.message", "component") + @publish('foo.some.message', 'component') someMethod() { - return { - my: "data" - }; + return { my: 'data' }; } - @publish("foo.some.other") + @publish('foo.some.other') anotherMethod() { // ... } } + +let foo = new FooComponent(); + +foo.someMethod(); +foo.anotherMethod(); ``` 以后,只要调用`someMethod`或者`anotherMethod`,就会自动发出一个事件。 -```javascript -let foo = new FooComponent(); - -foo.someMethod() // 在"component"频道发布"foo.some.message"事件,附带的数据是{ my: "data" } -foo.anotherMethod() // 在"/"频道发布"foo.some.other"事件,不附带数据 +```bash +$ bash-node index.js +频道: component +事件: foo.some.message +数据: { my: 'data' } + +频道: / +事件: foo.some.other +数据: undefined ``` ## Mixin From b424224cb78cdeffc1d0ad47de642869ecbb762b Mon Sep 17 00:00:00 2001 From: ruanyf Date: Tue, 21 Nov 2017 13:38:59 +0800 Subject: [PATCH 031/595] docs(set): edit set --- docs/set-map.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/set-map.md b/docs/set-map.md index a8753cfbe..2f7040f01 100644 --- a/docs/set-map.md +++ b/docs/set-map.md @@ -249,7 +249,7 @@ let unique = [...new Set(arr)]; // [3, 5, 2] ``` -而且,数组的`map`和`filter`方法也可以用于 Set 了。 +而且,数组的`map`和`filter`方法也可以间接用于 Set 了。 ```javascript let set = new Set([1, 2, 3]); From 7dd83344b6b5b208c833019ce8aef2bd0a5ee238 Mon Sep 17 00:00:00 2001 From: Jing Ma Date: Wed, 22 Nov 2017 11:50:53 +0800 Subject: [PATCH 032/595] Updated result of examples --- docs/object.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/object.md b/docs/object.md index bec0117f5..baa1d44dc 100644 --- a/docs/object.md +++ b/docs/object.md @@ -707,7 +707,7 @@ Object.getOwnPropertyDescriptors(obj) // enumerable: true, // configurable: true }, // bar: -// { get: [Function: bar], +// { get: [Function: get bar], // set: undefined, // enumerable: true, // configurable: true } } @@ -761,7 +761,7 @@ const target2 = {}; Object.defineProperties(target2, Object.getOwnPropertyDescriptors(source)); Object.getOwnPropertyDescriptor(target2, 'foo') // { get: undefined, -// set: [Function: foo], +// set: [Function: set foo], // enumerable: true, // configurable: true } ``` From bfa394a2d72d66531707377ebaf918371a042eee Mon Sep 17 00:00:00 2001 From: ruanyf Date: Thu, 23 Nov 2017 16:59:21 +0800 Subject: [PATCH 033/595] docs(style): fix typo --- docs/style.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/style.md b/docs/style.md index 375377c5a..254850d7e 100644 --- a/docs/style.md +++ b/docs/style.md @@ -43,7 +43,7 @@ if (true) { 在`let`和`const`之间,建议优先使用`const`,尤其是在全局环境,不应该设置变量,只应设置常量。 -`const`优于`let`有几个原因。一个是`const`可以提醒阅读程序的人,这个变量不应该改变;另一个是`const`比较符合函数式编程思想,运算不改变值,只是新建值,而且这样也有利于将来的分布式运算;最后一个原因是 JavaScript 编译器会对`const`进行优化,所以多使用`const`,有利于提供程序的运行效率,也就是说`let`和`const`的本质区别,其实是编译器内部的处理不同。 +`const`优于`let`有几个原因。一个是`const`可以提醒阅读程序的人,这个变量不应该改变;另一个是`const`比较符合函数式编程思想,运算不改变值,只是新建值,而且这样也有利于将来的分布式运算;最后一个原因是 JavaScript 编译器会对`const`进行优化,所以多使用`const`,有利于提高程序的运行效率,也就是说`let`和`const`的本质区别,其实是编译器内部的处理不同。 ```javascript // bad From d808c68ce0b8ac62981838312faf7af422b3cf90 Mon Sep 17 00:00:00 2001 From: ruanyf Date: Fri, 24 Nov 2017 14:44:51 +0800 Subject: [PATCH 034/595] docs(generator): add generator context --- docs/generator.md | 26 +++++++++++++++++++++++++- 1 file changed, 25 insertions(+), 1 deletion(-) diff --git a/docs/generator.md b/docs/generator.md index 163437f66..d8bc69d95 100644 --- a/docs/generator.md +++ b/docs/generator.md @@ -6,7 +6,7 @@ Generator 函数是 ES6 提供的一种异步编程解决方案,语法行为与传统函数完全不同。本章详细介绍 Generator 函数的语法和 API,它的异步编程应用请看《Generator 函数的异步应用》一章。 -Generator 函数有多种理解角度。从语法上,首先可以把它理解成,Generator 函数是一个状态机,封装了多个内部状态。 +Generator 函数有多种理解角度。语法上,首先可以把它理解成,Generator 函数是一个状态机,封装了多个内部状态。 执行 Generator 函数会返回一个遍历器对象,也就是说,Generator 函数除了状态机,还是一个遍历器对象生成函数。返回的遍历器对象,可以依次遍历 Generator 函数内部的每一个状态。 @@ -1216,6 +1216,30 @@ Generator 函数是 ES6 对协程的实现,但属于不完全实现。Generato 如果将 Generator 函数当作协程,完全可以将多个需要互相协作的任务写成 Generator 函数,它们之间使用`yield`表示式交换控制权。 +### Generator 与上下文 + +JavaScript 代码运行时,会产生一个全局的上下文环境(context,又称运行环境),包含了当前所有的变量和对象。然后,执行函数(或块级代码)的时候,又会在当前上下文环境的上层,产生一个函数运行的上下文,变成当前(active)的上下文,由此形成一个上下文环境的堆栈(context stack)。 + +这个堆栈是“后进先出”的数据结构,最后产生的上下文环境首先执行完成,退出堆栈,然后再执行完成它下层的上下文,直至所有代码执行完成,堆栈清空。 + +Generator 函数不是这样,它执行产生的上下文环境,一旦遇到`yield`命令,就会暂时退出堆栈,但是并不消失,里面的所有变量和对象会冻结在当前状态。等到对它执行`next`命令时,这个上下文环境又会重新加入调用栈,冻结的变量和对象恢复执行。 + +```javascript +function *gen() { + yield 1; + return 2; +} + +let g = gen(); + +console.log( + g.next().value, + g.next().value, +); +``` + +上面代码中,第一次执行`g.next()`时,Generator 函数`gen`的上下文会加入堆栈,即开始运行`gen`内部的代码。等遇到`yield 1`时,`gen`上下文退出堆栈,内部状态冻结。第二次执行`g.next()`时,`gen`上下文重新加入堆栈,变成当前的上下文,重新恢复执行。 + ## 应用 Generator 可以暂停函数执行,返回任意表达式的值。这种特点使得 Generator 有多种应用场景。 From 3f28d7b06ec0e4647b9f2be91221f24f55741b61 Mon Sep 17 00:00:00 2001 From: Jing Ma Date: Fri, 24 Nov 2017 16:17:31 +0800 Subject: [PATCH 035/595] Updated example and explanation of Symbol.species --- docs/symbol.md | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/docs/symbol.md b/docs/symbol.md index 11c5384c8..bfcc16344 100644 --- a/docs/symbol.md +++ b/docs/symbol.md @@ -591,11 +591,14 @@ class MyArray extends Array { let a = new MyArray(1,2,3); let mapped = a.map(x => x * x); +a instanceof MyArray // true +a instanceof Array // true + mapped instanceof MyArray // false mapped instanceof Array // true ``` -上面代码中,由于构造函数被替换成了`Array`。所以,`mapped`对象不是`MyArray`的实例,而是`Array`的实例。 +上面代码中,`a`是`MyArray`的实例,所以`a instanceof MyArray`返回`true`。由于构造函数被替换成了`Array`,所以`a`实际上也是`Array`的实例,于是`a instanceof Array`也返回`true`。而`mapped`是`Array.prototype.map`运算的结果,已经是真正的数组,它是`Array`的实例,而不是`MyArray`的实例,于是`mapped instanceof Array`返回`true`,而`mapped instanceof MyArray`返回`false`。 ### Symbol.match From 075c27b54dccdb6466ab0613373bdbd7b06b4162 Mon Sep 17 00:00:00 2001 From: ruanyf Date: Tue, 5 Dec 2017 21:42:30 +0800 Subject: [PATCH 036/595] docs(decorator): edit method decorator --- docs/decorator.md | 6 ++++-- docs/proxy.md | 2 +- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/docs/decorator.md b/docs/decorator.md index 15cf50ce1..484363439 100644 --- a/docs/decorator.md +++ b/docs/decorator.md @@ -148,7 +148,7 @@ class Person { 上面代码中,修饰器`readonly`用来修饰“类”的`name`方法。 -此时,修饰器函数一共可以接受三个参数,第一个参数是所要修饰的目标对象,即类的实例(这不同于类的修饰,那种情况时`target`参数指的是类本身);第二个参数是所要修饰的属性名,第三个参数是该属性的描述对象。 +修饰器函数`readonly`一共可以接受三个参数。 ```javascript function readonly(target, name, descriptor){ @@ -168,7 +168,9 @@ readonly(Person.prototype, 'name', descriptor); Object.defineProperty(Person.prototype, 'name', descriptor); ``` -上面代码说明,修饰器(readonly)会修改属性的描述对象(descriptor),然后被修改的描述对象再用来定义属性。 +修饰器第一个参数是类的原型对象,上例是`Person.prototype`,修饰器的本意是要“修饰”类的实例,但是这个时候实例还没生成,所以只能去修饰原型(这不同于类的修饰,那种情况时`target`参数指的是类本身);第二个参数是所要修饰的属性名,第三个参数是该属性的描述对象。 + +另外,上面代码说明,修饰器(readonly)会修改属性的描述对象(descriptor),然后被修改的描述对象再用来定义属性。 下面是另一个例子,修改属性描述对象的`enumerable`属性,使得该属性不可遍历。 diff --git a/docs/proxy.md b/docs/proxy.md index d890d6c8a..d6b777292 100644 --- a/docs/proxy.md +++ b/docs/proxy.md @@ -530,7 +530,7 @@ for (let b in oproxy2) { // 99 ``` -上面代码中,`has`拦截只对`in`循环生效,对`for...in`循环不生效,导致不符合要求的属性没有被排除在`for...in`循环之外。 +上面代码中,`has`拦截只对`in`运算符生效,对`for...in`循环不生效,导致不符合要求的属性没有被排除在`for...in`循环之外。 ### construct() From 48ccd23c174e80ddc50e962ddbd4efe75751fc58 Mon Sep 17 00:00:00 2001 From: isLishude Date: Wed, 6 Dec 2017 09:32:15 +0800 Subject: [PATCH 037/595] =?UTF-8?q?=E5=A2=9E=E5=8A=A0ES5=E4=B8=ADreduce?= =?UTF-8?q?=E6=96=B9=E6=B3=95=E5=AF=B9=E6=95=B0=E7=BB=84=E7=A9=BA=E4=BD=8D?= =?UTF-8?q?=E7=9A=84=E5=A4=84=E7=90=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ```js [1,,2].reduce((x,y)=>{ console.log(x,y) return x+y }) // 输出 // 1 2 // 3 ``` --- docs/array.md | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/docs/array.md b/docs/array.md index 3adaaed55..dd2379e4d 100644 --- a/docs/array.md +++ b/docs/array.md @@ -728,7 +728,7 @@ Array(3) // [, , ,] ES5 对空位的处理,已经很不一致了,大多数情况下会忽略空位。 -- `forEach()`, `filter()`, `every()` 和`some()`都会跳过空位。 +- `forEach()`, `filter()`, `reduce()`, `every()` 和`some()`都会跳过空位。 - `map()`会跳过空位,但会保留这个值 - `join()`和`toString()`会将空位视为`undefined`,而`undefined`和`null`会被处理成空字符串。 @@ -742,6 +742,9 @@ ES5 对空位的处理,已经很不一致了,大多数情况下会忽略空 // every方法 [,'a'].every(x => x==='a') // true +// reduce方法 +[1,,2].reduce((x,y) => return x+y) // 3 + // some方法 [,'a'].some(x => x !== 'a') // false From 22c3b931dcad23a03ea3d3a9dc3408db9637f4f9 Mon Sep 17 00:00:00 2001 From: ruanyf Date: Thu, 7 Dec 2017 10:26:10 +0800 Subject: [PATCH 038/595] docs(proxy): edit set() --- docs/proxy.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/proxy.md b/docs/proxy.md index d6b777292..e71e40d48 100644 --- a/docs/proxy.md +++ b/docs/proxy.md @@ -331,7 +331,7 @@ let validator = { } } - // 对于age以外的属性,直接保存 + // 对于满足条件的 age 属性以及其他属性,直接保存 obj[prop] = value; } }; From 465b82597a27cebbfe0bf039877557f0549cc42e Mon Sep 17 00:00:00 2001 From: ruanyf Date: Mon, 11 Dec 2017 05:04:01 +0800 Subject: [PATCH 039/595] docs(module-loader): fix typo --- docs/generator-async.md | 2 +- docs/module-loader.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/generator-async.md b/docs/generator-async.md index 319f740bc..fd5200953 100644 --- a/docs/generator-async.md +++ b/docs/generator-async.md @@ -219,7 +219,7 @@ Thunk 函数早在上个世纪 60 年代就诞生了。 ```javascript var x = 1; -function f(m){ +function f(m) { return m * 2; } diff --git a/docs/module-loader.md b/docs/module-loader.md index da04d39c8..6d196ef52 100644 --- a/docs/module-loader.md +++ b/docs/module-loader.md @@ -464,7 +464,7 @@ console.log(es_namespace.default); // { bar:'my-default' } ``` -上面代码中,`default`接口变成了`es_namespace.default`属性。另外,由于存在缓存机制,`es.js`对`foo`的重新赋值没有在模块外部反映出来。 +上面代码中,`default`接口变成了`es_namespace.default`属性。另外,由于存在缓存机制,`es.mjs`对`foo`的重新赋值没有在模块外部反映出来。 下面是另一个例子。 From 0213ffc1abf4c5d3fc0e575a8df3671bc71c4c56 Mon Sep 17 00:00:00 2001 From: Open Next Date: Wed, 13 Dec 2017 15:33:14 +0800 Subject: [PATCH 040/595] =?UTF-8?q?docs(style):=20modify=20the=20bold=20fo?= =?UTF-8?q?rmat=20of=20=20-=EF=BC=883=EF=BC=89yield?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit remove needless space char in **(3)yield* ** --- docs/iterator.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/iterator.md b/docs/iterator.md index 67b883c14..60d193d53 100644 --- a/docs/iterator.md +++ b/docs/iterator.md @@ -363,7 +363,7 @@ let arr = ['b', 'c']; let arr = [...iterable]; ``` -**(3)yield\* ** +**(3)yield\*** `yield*`后面跟的是一个可遍历的结构,它会调用该结构的遍历器接口。 From 649b29779a33f291b70de3bd765a5ce9330b28df Mon Sep 17 00:00:00 2001 From: ruanyf Date: Thu, 21 Dec 2017 19:16:44 +0800 Subject: [PATCH 041/595] docs(symbol): edit symbol.species #506 --- docs/symbol.md | 51 +++++++++++++++++++++++++++++++++++++------------- 1 file changed, 38 insertions(+), 13 deletions(-) diff --git a/docs/symbol.md b/docs/symbol.md index bfcc16344..f88817299 100644 --- a/docs/symbol.md +++ b/docs/symbol.md @@ -563,18 +563,27 @@ a2[1] = 6; ### Symbol.species -对象的`Symbol.species`属性,指向当前对象的构造函数。创造实例时,默认会调用这个方法,即使用这个属性返回的函数当作构造函数,来创造新的实例对象。 +对象的`Symbol.species`属性,指向一个构造函数。创建造衍生对象时,会使用该属性。 ```javascript class MyArray extends Array { - // 覆盖父类 Array 的构造函数 - static get [Symbol.species]() { return Array; } } + +const a = new MyArray(); +a.map(x => x) instanceof MyArray // true ``` -上面代码中,子类`MyArray`继承了父类`Array`。创建`MyArray`的实例对象时,本来会调用它自己的构造函数(本例中被省略了),但是由于定义了`Symbol.species`属性,所以会使用这个属性返回的的函数,创建`MyArray`的实例。 +上面代码中,子类`MyArray`继承了父类`Array`。`a.map(x => x)`会创建一个`MyArray`的衍生对象,该衍生对象还是`MyArray`的实例。 + +现在,`MyArray`设置`Symbol.species`属性。 + +```javascript +class MyArray extends Array { + static get [Symbol.species]() { return Array; } +} +``` -这个例子也说明,定义`Symbol.species`属性要采用`get`读取器。默认的`Symbol.species`属性等同于下面的写法。 +上面代码中,由于定义了`Symbol.species`属性,创建衍生对象时就会使用这个属性返回的的函数,作为构造函数。这个例子也说明,定义`Symbol.species`属性要采用`get`读取器。默认的`Symbol.species`属性等同于下面的写法。 ```javascript static get [Symbol.species]() { @@ -582,23 +591,39 @@ static get [Symbol.species]() { } ``` -下面是一个例子。 +现在,再来看前面的例子。 ```javascript class MyArray extends Array { static get [Symbol.species]() { return Array; } } -let a = new MyArray(1,2,3); -let mapped = a.map(x => x * x); -a instanceof MyArray // true -a instanceof Array // true +const a = new MyArray(); +a.map(x => x) instanceof MyArray // false +a.map(x => x) instanceof Array // true +``` + +上面代码中,`a.map(x => x)`创建的衍生对象,就不是`MyArray`的实例,而直接就是`Array`的实例。 + +再看一个例子。 + +```javascript +class T1 extends Promise { +} -mapped instanceof MyArray // false -mapped instanceof Array // true +class T2 extends Promise { + static get [Symbol.species]() { + return Promise; + } +} + +new T1(r => r()).then(v => v) instanceof T1 // true +new T2(r => r()).then(v => v) instanceof T2 // false ``` -上面代码中,`a`是`MyArray`的实例,所以`a instanceof MyArray`返回`true`。由于构造函数被替换成了`Array`,所以`a`实际上也是`Array`的实例,于是`a instanceof Array`也返回`true`。而`mapped`是`Array.prototype.map`运算的结果,已经是真正的数组,它是`Array`的实例,而不是`MyArray`的实例,于是`mapped instanceof Array`返回`true`,而`mapped instanceof MyArray`返回`false`。 +上面代码中,`T2`定义了`Symbol.species`属性,`T1`没有。结果就导致了创建衍生对象时(`then`方法),`T1`调用的是自身的构造方法,而`T2`调用的是`Promise`的构造方法。 + +总之,`Symbol.species`的作用在于,实例对象在运行过程中,需要再次调用自身的构造函数时,会调用该属性指定的构造函数。它主要的用途是,有些类库是在基类的基础上修改的,那么子类使用继承的方法时,作者可能希望返回基类的实例,而不是子类的实例。 ### Symbol.match From 5bda2fdb53082132005d6c87463b756cc6c95e89 Mon Sep 17 00:00:00 2001 From: zhangbao Date: Tue, 26 Dec 2017 09:03:51 +0800 Subject: [PATCH 042/595] docs:Fix typo The type of `value` is number, not string. --- docs/iterator.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/iterator.md b/docs/iterator.md index 60d193d53..30c2e55ed 100644 --- a/docs/iterator.md +++ b/docs/iterator.md @@ -69,9 +69,9 @@ function makeIterator(array) { ```javascript var it = idMaker(); -it.next().value // '0' -it.next().value // '1' -it.next().value // '2' +it.next().value // 0 +it.next().value // 1 +it.next().value // 2 // ... function idMaker() { From 311cfaa50d93a50ca601de8dca5b4c916dccee09 Mon Sep 17 00:00:00 2001 From: zhangbao Date: Tue, 26 Dec 2017 10:55:39 +0800 Subject: [PATCH 043/595] =?UTF-8?q?Fix=EF=BC=9A=E7=BB=9F=E4=B8=80=E4=BE=8B?= =?UTF-8?q?=E5=AD=90=E4=B8=AD=E7=9A=84=E4=BB=A3=E7=A0=81=E9=A3=8E=E6=A0=BC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 更能清晰地分辨出是对象字面量声明中的方法简写 --- docs/iterator.md | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/docs/iterator.md b/docs/iterator.md index 60d193d53..d64aea224 100644 --- a/docs/iterator.md +++ b/docs/iterator.md @@ -444,13 +444,13 @@ str // "hi" `Symbol.iterator`方法的最简单实现,还是使用下一章要介绍的 Generator 函数。 ```javascript -var myIterable = {}; - -myIterable[Symbol.iterator] = function* () { - yield 1; - yield 2; - yield 3; -}; +let myIterable = { + [Symbol.iterator]: function* () { + yield 1; + yield 2; + yield 3; + } +} [...myIterable] // [1, 2, 3] // 或者采用下面的简洁写法 @@ -465,8 +465,8 @@ let obj = { for (let x of obj) { console.log(x); } -// hello -// world +// "hello" +// "world" ``` 上面代码中,`Symbol.iterator`方法几乎不用部署任何代码,只要用 yield 命令给出每一步的返回值即可。 From 6b647bd7d7f6f254941a9477ab2938b20bbade57 Mon Sep 17 00:00:00 2001 From: zhangbao Date: Tue, 26 Dec 2017 11:11:47 +0800 Subject: [PATCH 044/595] Fix typo --- docs/iterator.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/iterator.md b/docs/iterator.md index 60d193d53..5f814605c 100644 --- a/docs/iterator.md +++ b/docs/iterator.md @@ -108,9 +108,9 @@ interface IterationResult { Iterator 接口的目的,就是为所有数据结构,提供了一种统一的访问机制,即`for...of`循环(详见下文)。当使用`for...of`循环遍历某种数据结构时,该循环会自动去寻找 Iterator 接口。 -一种数据结构只要部署了 Iterator 接口,我们就称这种数据结构是”可遍历的“(iterable)。 +一种数据结构只要部署了 Iterator 接口,我们就称这种数据结构是“可遍历的”(iterable)。 -ES6 规定,默认的 Iterator 接口部署在数据结构的`Symbol.iterator`属性,或者说,一个数据结构只要具有`Symbol.iterator`属性,就可以认为是“可遍历的”(iterable)。`Symbol.iterator`属性本身是一个函数,就是当前数据结构默认的遍历器生成函数。执行这个函数,就会返回一个遍历器。至于属性名`Symbol.iterator`,它是一个表达式,返回`Symbol`对象的`iterator`属性,这是一个预定义好的、类型为 Symbol 的特殊值,所以要放在方括号内(参见 Symbol 一章)。 +ES6 规定,默认的 Iterator 接口部署在数据结构的`Symbol.iterator`属性,或者说,一个数据结构只要具有`Symbol.iterator`属性,就可以认为是“可遍历的”(iterable)。`Symbol.iterator`属性本身是一个函数,就是当前数据结构默认的遍历器生成函数。执行这个函数,就会返回一个遍历器。至于属性名`Symbol.iterator`,它是一个表达式,返回`Symbol`对象的`iterator`属性,这是一个预定义好的、类型为 Symbol 的特殊值,所以要放在方括号内(参见《Symbol》一章)。 ```javascript const obj = { @@ -573,7 +573,7 @@ for (let a of arr) { } ``` -上面代码表明,`for...in`循环读取键名,`for...of`循环读取键值。如果要通过`for...of`循环,获取数组的索引,可以借助数组实例的`entries`方法和`keys`方法,参见《数组的扩展》章节。 +上面代码表明,`for...in`循环读取键名,`for...of`循环读取键值。如果要通过`for...of`循环,获取数组的索引,可以借助数组实例的`entries`方法和`keys`方法(参见《数组的扩展》一章)。 `for...of`循环调用遍历器接口,数组的遍历器接口只返回具有数字索引的属性。这一点跟`for...in`循环也不一样。 From 1ff6639dfab280bf828a2e5634647d5ccc101252 Mon Sep 17 00:00:00 2001 From: ruanyf Date: Fri, 29 Dec 2017 15:55:22 +0800 Subject: [PATCH 045/595] docs(regex): edit y flag --- docs/array.md | 4 ++-- docs/regex.md | 22 ---------------------- 2 files changed, 2 insertions(+), 24 deletions(-) diff --git a/docs/array.md b/docs/array.md index dd2379e4d..b57ec7e28 100644 --- a/docs/array.md +++ b/docs/array.md @@ -346,8 +346,8 @@ let arr2 = Array.from(arrayLike); // ['a', 'b', 'c'] ```javascript // NodeList对象 let ps = document.querySelectorAll('p'); -Array.from(ps).forEach(function (p) { - console.log(p); +Array.from(ps).filter(p => { + return p.textContent.length > 100; }); // arguments对象 diff --git a/docs/regex.md b/docs/regex.md index 91a57dae5..11d238f61 100644 --- a/docs/regex.md +++ b/docs/regex.md @@ -216,28 +216,6 @@ REGEX.lastIndex // 4 上面代码由于不能保证头部匹配,所以返回`null`。`y`修饰符的设计本意,就是让头部匹配的标志`^`在全局匹配中都有效。 -在`split`方法中使用`y`修饰符,原字符串必须以分隔符开头。这也意味着,只要匹配成功,数组的第一个成员肯定是空字符串。 - -```javascript -// 没有找到匹配 -'x##'.split(/#/y) -// [ 'x##' ] - -// 找到两个匹配 -'##x'.split(/#/y) -// [ '', '', 'x' ] -``` - -后续的分隔符只有紧跟前面的分隔符,才会被识别。 - -```javascript -'#x#'.split(/#/y) -// [ '', 'x#' ] - -'##'.split(/#/y) -// [ '', '', '' ] -``` - 下面是字符串对象的`replace`方法的例子。 ```javascript From 95caa944a3adacaf826fed60e4548b8d0b2c1ee6 Mon Sep 17 00:00:00 2001 From: zhangbao Date: Mon, 1 Jan 2018 18:13:54 +0800 Subject: [PATCH 046/595] =?UTF-8?q?docs(destructuring):=20=E4=BF=AE?= =?UTF-8?q?=E6=94=B9=E5=8F=A5=E5=BC=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 将否定句改为肯定句,更能快速理解。 --- docs/destructuring.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/destructuring.md b/docs/destructuring.md index 0648f5dee..3d1cc7340 100644 --- a/docs/destructuring.md +++ b/docs/destructuring.md @@ -130,7 +130,7 @@ foo // true [x, y = 'b'] = ['a', undefined]; // x='a', y='b' ``` -注意,ES6内部使用严格相等运算符(`===`),判断一个位置是否有值。所以,如果一个数组成员不严格等于`undefined`,默认值是不会生效的。 +注意,ES6内部使用严格相等运算符(`===`),判断一个位置是否有值。而且,只有当一个数组成员严格等于`undefined`,默认值才会生效。 ```javascript var [x = 1] = [undefined]; From 083c99e838b487d18642042bf9c33ead93c57a18 Mon Sep 17 00:00:00 2001 From: zhangbao Date: Mon, 1 Jan 2018 18:23:16 +0800 Subject: [PATCH 047/595] =?UTF-8?q?docs(destructuring):=20=E8=A1=A5?= =?UTF-8?q?=E5=85=A8=E6=8A=A5=E9=94=99=E4=BF=A1=E6=81=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 这样直接看代码的话,更能直观了解到要先声明 y 才能使用它,而不是在声明 y 前就使用它。 --- docs/destructuring.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/destructuring.md b/docs/destructuring.md index 3d1cc7340..82acefbb7 100644 --- a/docs/destructuring.md +++ b/docs/destructuring.md @@ -169,10 +169,10 @@ if ([1][0] === undefined) { let [x = 1, y = x] = []; // x=1; y=1 let [x = 1, y = x] = [2]; // x=2; y=2 let [x = 1, y = x] = [1, 2]; // x=1; y=2 -let [x = y, y = 1] = []; // ReferenceError +let [x = y, y = 1] = []; // ReferenceError: y is not defined ``` -上面最后一个表达式之所以会报错,是因为`x`用到默认值`y`时,`y`还没有声明。 +上面最后一个表达式之所以会报错,是因为`x`用`y`做默认值时,`y`还没有声明。 ## 对象的解构赋值 From 73cd2a2f92e94f47c943099817b88be1c75be0f4 Mon Sep 17 00:00:00 2001 From: zhangbao Date: Mon, 1 Jan 2018 18:37:57 +0800 Subject: [PATCH 048/595] docs(destructuring): fix typo MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 修改了两处: 第一处:去掉假设词“如果”;明确`null`是个有效的赋值,它导致了默认值`3`失效。 第二处:参考章节字样使用圆括号括起来了。 --- docs/destructuring.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/destructuring.md b/docs/destructuring.md index 82acefbb7..ba8d5058f 100644 --- a/docs/destructuring.md +++ b/docs/destructuring.md @@ -324,7 +324,7 @@ var {x = 3} = {x: null}; x // null ``` -上面代码中,如果`x`属性等于`null`,就不严格相等于`undefined`,导致默认值不会生效。 +上面代码中,属性`x`等于`null`,因为`null`与`undefined`不严格相等,所以是个有效的赋值,导致默认值`3`不会生效。 如果解构失败,变量的值等于`undefined`。 @@ -392,7 +392,7 @@ first // 1 last // 3 ``` -上面代码对数组进行对象解构。数组`arr`的`0`键对应的值是`1`,`[arr.length - 1]`就是`2`键,对应的值是`3`。方括号这种写法,属于“属性名表达式”,参见《对象的扩展》一章。 +上面代码对数组进行对象解构。数组`arr`的`0`键对应的值是`1`,`[arr.length - 1]`就是`2`键,对应的值是`3`。方括号这种写法,属于“属性名表达式”(参见《对象的扩展》一章)。 ## 字符串的解构赋值 From 686fb84a3276fbc619ef521e0a8e303d1d26a8ee Mon Sep 17 00:00:00 2001 From: zhangbao Date: Mon, 1 Jan 2018 21:56:43 +0800 Subject: [PATCH 049/595] =?UTF-8?q?docs(module):=20=E4=BF=AE=E6=94=B9?= =?UTF-8?q?=E5=8F=A5=E5=BC=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 较为清楚地表达意思。 --- docs/module.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/module.md b/docs/module.md index 4c9add1c1..c27fa18ef 100644 --- a/docs/module.md +++ b/docs/module.md @@ -390,7 +390,7 @@ import {crc32} from 'crc32'; // 输入 上面代码的两组写法,第一组是使用`export default`时,对应的`import`语句不需要使用大括号;第二组是不使用`export default`时,对应的`import`语句需要使用大括号。 -`export default`命令用于指定模块的默认输出。显然,一个模块只能有一个默认输出,因此`export default`命令只能使用一次。所以,`import`命令后面才不用加大括号,因为只可能对应一个方法。 +`export default`命令用于指定模块的默认输出。显然,一个模块只能有一个默认输出,因此`export default`命令只能使用一次。所以,import命令后面才不用加大括号,因为只可能唯一对应`export default`命令。 本质上,`export default`就是输出一个叫做`default`的变量或方法,然后系统允许你为它取任意名字。所以,下面的写法是有效的。 @@ -425,7 +425,7 @@ export default var a = 1; 上面代码中,`export default a`的含义是将变量`a`的值赋给变量`default`。所以,最后一种写法会报错。 -同样地,因为`export default`本质是将该命令后面的值,赋给`default`变量以后再默认,所以直接将一个值写在`export default`之后。 +同样地,因为`export default`命令的本质是将后面的值,赋给`default`变量,所以可以直接将一个值写在`export default`之后。 ```javascript // 正确 From edc4ff260cb8511bdd25bc92643c936b7214d590 Mon Sep 17 00:00:00 2001 From: zhangbao Date: Tue, 2 Jan 2018 15:03:01 +0800 Subject: [PATCH 050/595] =?UTF-8?q?doc(destructuring):=20=E8=8B=B1?= =?UTF-8?q?=E6=96=87=E5=8D=95=E8=AF=8D=E4=B8=8E=E4=B8=AD=E6=96=87=E4=B9=8B?= =?UTF-8?q?=E9=97=B4=E7=A9=BA=E4=B8=80=E6=A0=BC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/destructuring.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/destructuring.md b/docs/destructuring.md index a06d1e7ee..709b51a7c 100644 --- a/docs/destructuring.md +++ b/docs/destructuring.md @@ -122,7 +122,7 @@ let [x, y = 'b'] = ['a']; // x='a', y='b' let [x, y = 'b'] = ['a', undefined]; // x='a', y='b' ``` -注意,ES6内部使用严格相等运算符(`===`),判断一个位置是否有值。而且,只有当一个数组成员严格等于`undefined`,默认值才会生效。 +注意,ES6 内部使用严格相等运算符(`===`),判断一个位置是否有值。而且,只有当一个数组成员严格等于`undefined`,默认值才会生效。 ```javascript let [x = 1] = [undefined]; From 1ed4d662781ed785a2271391c9dcb3ac22badeb6 Mon Sep 17 00:00:00 2001 From: zhangbao Date: Tue, 2 Jan 2018 15:12:39 +0800 Subject: [PATCH 051/595] docs(destructuring): fix typo --- docs/destructuring.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/destructuring.md b/docs/destructuring.md index 709b51a7c..5f69ec7e3 100644 --- a/docs/destructuring.md +++ b/docs/destructuring.md @@ -122,7 +122,7 @@ let [x, y = 'b'] = ['a']; // x='a', y='b' let [x, y = 'b'] = ['a', undefined]; // x='a', y='b' ``` -注意,ES6 内部使用严格相等运算符(`===`),判断一个位置是否有值。而且,只有当一个数组成员严格等于`undefined`,默认值才会生效。 +注意,ES6 内部使用严格相等运算符(`===`),判断一个位置是否有值。所以,只有当一个数组成员严格等于`undefined`,默认值才会生效。 ```javascript let [x = 1] = [undefined]; From 3eeb90b85c9f770376b9e12142282b1b749caa3c Mon Sep 17 00:00:00 2001 From: ruanyf Date: Tue, 2 Jan 2018 17:29:38 +0800 Subject: [PATCH 052/595] docs(class): edit private method #572 --- docs/class.md | 30 +++++++++++++++++++++++++++--- docs/reference.md | 1 + 2 files changed, 28 insertions(+), 3 deletions(-) diff --git a/docs/class.md b/docs/class.md index 927a38e7e..f3171c5a6 100644 --- a/docs/class.md +++ b/docs/class.md @@ -374,7 +374,9 @@ class Foo {} 上面的代码不会报错,因为`Bar`继承`Foo`的时候,`Foo`已经有定义了。但是,如果存在`class`的提升,上面代码就会报错,因为`class`会被提升到代码头部,而`let`命令是不提升的,所以导致`Bar`继承`Foo`的时候,`Foo`还没有定义。 -## 私有方法 +## 私有方法和私有属性 + +### 现有的方法 私有方法是常见需求,但 ES6 不提供,只能通过变通方法模拟实现。 @@ -441,9 +443,9 @@ export default class myClass{ 上面代码中,`bar`和`snaf`都是`Symbol`值,导致第三方无法获取到它们,因此达到了私有方法和私有属性的效果。 -## 私有属性 +### 私有属性的提案 -与私有方法一样,ES6 不支持私有属性。目前,有一个[提案](https://github.com/tc39/proposal-class-fields#private-fields),为`class`加了私有属性。方法是在属性名之前,使用`#`表示。 +与私有方法一样,ES6 不支持私有属性。目前,有一个[提案](https://github.com/tc39/proposal-private-methods),为`class`加了私有属性。方法是在属性名之前,使用`#`表示。 ```javascript class Point { @@ -485,6 +487,28 @@ class Foo { } ``` +上面代码中,`#sum()`就是一个私有方法。 + +另外,私有属性也可以设置 getter 和 setter 方法。 + +```javascript +class Counter { + #xValue = 0; + + get #x() { return #xValue; } + set #x(value) { + this.#xValue = value; + } + + constructor() { + super(); + // ... + } +} +``` + +上面代码中,`#x`是一个私有属性,它的读写都通过`get #x()`和`set #x()`来完成。 + ## this 的指向 类的方法内部如果含有`this`,它默认指向类的实例。但是,必须非常小心,一旦单独使用该方法,很可能报错。 diff --git a/docs/reference.md b/docs/reference.md index a1878dcf7..a60a38846 100644 --- a/docs/reference.md +++ b/docs/reference.md @@ -185,6 +185,7 @@ - Jake Archibald, [Async functions - making promises friendly](https://developers.google.com/web/fundamentals/getting-started/primers/async-functions) - Axel Rauschmayer, [ES proposal: asynchronous iteration](http://www.2ality.com/2016/10/asynchronous-iteration.html): 异步遍历器的详细介绍 - Dima Grossman, [How to write async await without try-catch blocks in Javascript](http://blog.grossman.io/how-to-write-async-await-without-try-catch-blocks-in-javascript/): 除了 try/catch 以外的 async 函数内部捕捉错误的方法 +- Mostafa Gaafa, [6 Reasons Why JavaScript’s Async/Await Blows Promises Away](https://hackernoon.com/6-reasons-why-javascripts-async-await-blows-promises-away-tutorial-c7ec10518dd9): Async 函数的6个好处 ## Class From afe19e0e863a2bf51443f75e2646855fd7d3c909 Mon Sep 17 00:00:00 2001 From: picc-lu <32639909+picc-lu@users.noreply.github.com> Date: Tue, 2 Jan 2018 21:56:33 +0800 Subject: [PATCH 053/595] Update number.md --- docs/number.md | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/docs/number.md b/docs/number.md index 1311318e0..e9187d247 100644 --- a/docs/number.md +++ b/docs/number.md @@ -132,7 +132,7 @@ Number.parseFloat === parseFloat // true ## Number.isInteger() -`Number.isInteger()`用来判断一个值是否为整数。需要注意的是,在 JavaScript 内部,整数和浮点数是同样的储存方法,所以 3 和 3.0 被视为同一个值。 +`Number.isInteger()`用来判断一个值是否为整数。需要注意的是,在 JavaScript 内部,整数和浮点数采用的是同样的储存方法,所以 3 和 3.0 被视为同一个值。 ```javascript Number.isInteger(25) // true @@ -142,6 +142,16 @@ Number.isInteger("15") // false Number.isInteger(true) // false ``` +注意,由于 JavaScript 表示数值时最多只能存储 16 位十进制位数,超出位数上限时会导致精度丢失,导致误判。 + +```javascript +234 === 234 + 1e-14 // true +Number.isInteger(234.00000000000001) // true +// 3 位整数,14 位小数,一共17位,使最后一位1丢失,误判为true + + +``` + ES5 可以通过下面的代码,部署`Number.isInteger()`。 ```javascript From a79f879b5183cc0f9ae6d48b854cc2706f933788 Mon Sep 17 00:00:00 2001 From: picc-lu <32639909+picc-lu@users.noreply.github.com> Date: Tue, 2 Jan 2018 23:30:30 +0800 Subject: [PATCH 054/595] Update number.md --- docs/number.md | 24 ++++++++++++++++++++---- 1 file changed, 20 insertions(+), 4 deletions(-) diff --git a/docs/number.md b/docs/number.md index e9187d247..cdd6cdfe4 100644 --- a/docs/number.md +++ b/docs/number.md @@ -142,14 +142,30 @@ Number.isInteger("15") // false Number.isInteger(true) // false ``` -注意,由于 JavaScript 表示数值时最多只能存储 16 位十进制位数,超出位数上限时会导致精度丢失,导致误判。 +注意,由于 JavaScript 浮点数采用的是 IEEE 754 标准,表示数值时最多只能存储 52 位二进制位数(整数和小数的二进制总位数)。超出位数上限时,第 53 位会尝试是否往第 52 位进位(0 不进位,1 进位),它和它往后的位数一概丢弃,这种情况下可能会导致误判。 ```javascript -234 === 234 + 1e-14 // true -Number.isInteger(234.00000000000001) // true -// 3 位整数,14 位小数,一共17位,使最后一位1丢失,误判为true +3 === 3 + 2e-16 // true +Number.isInteger(3.0000000000000002) // true +// 3 的二进制 2 位,2e-16 的二进制最多能表示 50 位,第 3 位至 52 位全为 0,直到第 54 位才开始出现 1,而这一位被丢弃了,误判为 true +3 + 4e-16 === 3 + 6e-16 // true +Number.isInteger(3.0000000000000004) // false +Number.isInteger(3.0000000000000006) // false +// 第 50 位已为 1,且不会丢精度,所以 JavaScript 判定此数包含小数,返回 false +``` + +数值的整数部分为 0 时,其绝对值大于`Number.MIN_VALUE`就视为非整数。 + +```javascript +3 === 3 + 2e-16 // true +Number.isInteger(3.0000000000000002) // true +// 3 的二进制 2 位,2e-16 的二进制最多能表示 50 位,第 3 位至 52 位全为 0,直到第 54 位才开始出现 1,而这一位被丢弃了,误判为 true +3 + 4e-16 === 3 + 6e-16 // true +Number.isInteger(3.0000000000000004) // false +Number.isInteger(3.0000000000000006) // false +// 第 50 位已为 1,且不会丢精度,所以 JavaScript 判定此数包含小数,返回 false ``` ES5 可以通过下面的代码,部署`Number.isInteger()`。 From 90392b6e406832ede41d925787ac199e674cdaef Mon Sep 17 00:00:00 2001 From: gongpeione Date: Wed, 3 Jan 2018 00:36:05 +0800 Subject: [PATCH 055/595] =?UTF-8?q?Fix=20=E6=9B=BF=E4=BB=A3=E6=95=B0?= =?UTF-8?q?=E7=BB=84->=E6=9B=BF=E4=BB=A3=E5=87=BD=E6=95=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 扩展运算符应该是可以替代函数的 apply 方法。 --- docs/array.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/array.md b/docs/array.md index b57ec7e28..ebb5e9609 100644 --- a/docs/array.md +++ b/docs/array.md @@ -58,7 +58,7 @@ const arr = [ // [1] ``` -### 替代数组的 apply 方法 +### 替代函数的 apply 方法 由于扩展运算符可以展开数组,所以不再需要`apply`方法,将数组转为函数的参数了。 From 5fca18cb62fa938e2a718ae7de505c5a1b36734d Mon Sep 17 00:00:00 2001 From: picc-lu <32639909+picc-lu@users.noreply.github.com> Date: Wed, 3 Jan 2018 22:43:00 +0800 Subject: [PATCH 056/595] Update number.md --- docs/number.md | 28 +++++++++++++++++----------- 1 file changed, 17 insertions(+), 11 deletions(-) diff --git a/docs/number.md b/docs/number.md index cdd6cdfe4..7489e80e8 100644 --- a/docs/number.md +++ b/docs/number.md @@ -142,30 +142,36 @@ Number.isInteger("15") // false Number.isInteger(true) // false ``` -注意,由于 JavaScript 浮点数采用的是 IEEE 754 标准,表示数值时最多只能存储 52 位二进制位数(整数和小数的二进制总位数)。超出位数上限时,第 53 位会尝试是否往第 52 位进位(0 不进位,1 进位),它和它往后的位数一概丢弃,这种情况下可能会导致误判。 +注意,由于 JavaScript 浮点数采用的是 IEEE 754 标准,表示数值时最多只能存储 53 位二进制位数(1 位隐藏位与 52 位有效位)。超出位数上限时,第 54 位会尝试是否往第 53 位进位(0 不进位,1 进位),它和它往后的位数一概丢弃,这种情况下可能会导致误判。 ```javascript 3 === 3 + 2e-16 // true Number.isInteger(3.0000000000000002) // true -// 3 的二进制 2 位,2e-16 的二进制最多能表示 50 位,第 3 位至 52 位全为 0,直到第 54 位才开始出现 1,而这一位被丢弃了,误判为 true +// 3 的二进制 2 位,2e-16 的二进制最多能表示 51 位,第 3 ~ 53 位全为 0,直到第 55 位才开始出现 1,而这一位被丢弃了,误判为 true 3 + 4e-16 === 3 + 6e-16 // true Number.isInteger(3.0000000000000004) // false Number.isInteger(3.0000000000000006) // false -// 第 50 位已为 1,且不会丢精度,所以 JavaScript 判定此数包含小数,返回 false +// 第 51 位已为 1,且不会丢精度,所以 JavaScript 判定此数包含小数,返回 false ``` -数值的整数部分为 0 时,其绝对值大于`Number.MIN_VALUE`就视为非整数。 +数值的大小在 -1 与 1 之间(不含两个端点)时,其绝对值小于`Number.MIN_VALUE`即视为 0。 + +```javascript +Number.MIN_VALUE // 5e-324 +5e-325 === 0 // true +Number.isInteger(Number.MIN_VALUE) // false +Number.isInteger(5e-325) // true + +3e-324 === Number.MIN_VALUE // true +Number.isInteger(3e-324) // false +// 同样由于精度问题,即使是比`Number.MIN_VALUE`略小的数也会被判为 5e-324。 +``` + +因此,在金融、天文等领域的数据精度要求较高、判断值是否整数的情况下,不建议使用`Number.isInteger()`原生函数,请使用以下包含正则的函数惊醒处理: ```javascript -3 === 3 + 2e-16 // true -Number.isInteger(3.0000000000000002) // true -// 3 的二进制 2 位,2e-16 的二进制最多能表示 50 位,第 3 位至 52 位全为 0,直到第 54 位才开始出现 1,而这一位被丢弃了,误判为 true -3 + 4e-16 === 3 + 6e-16 // true -Number.isInteger(3.0000000000000004) // false -Number.isInteger(3.0000000000000006) // false -// 第 50 位已为 1,且不会丢精度,所以 JavaScript 判定此数包含小数,返回 false ``` ES5 可以通过下面的代码,部署`Number.isInteger()`。 From 3cc432075dcc0159d739211a9d2021ed94cad1cc Mon Sep 17 00:00:00 2001 From: picc-lu <32639909+picc-lu@users.noreply.github.com> Date: Wed, 3 Jan 2018 22:44:01 +0800 Subject: [PATCH 057/595] Update number.md --- docs/number.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/number.md b/docs/number.md index 7489e80e8..c88dcd194 100644 --- a/docs/number.md +++ b/docs/number.md @@ -168,7 +168,7 @@ Number.isInteger(3e-324) // false // 同样由于精度问题,即使是比`Number.MIN_VALUE`略小的数也会被判为 5e-324。 ``` -因此,在金融、天文等领域的数据精度要求较高、判断值是否整数的情况下,不建议使用`Number.isInteger()`原生函数,请使用以下包含正则的函数惊醒处理: +因此,在金融、天文等领域的数据精度要求较高、判断值是否整数的情况下,不建议使用`Number.isInteger()`原生函数,请使用以下包含正则的函数进行处理: ```javascript From dbcaa24a7fe48c0962ae152f703a20b74000eab8 Mon Sep 17 00:00:00 2001 From: picc-lu <32639909+picc-lu@users.noreply.github.com> Date: Thu, 4 Jan 2018 18:59:59 +0800 Subject: [PATCH 058/595] Update Number.isInteger function MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 基于 JavaSciprt 的浮点数 IEEE 754 标准进行深入分析。 --- docs/number.md | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/docs/number.md b/docs/number.md index c88dcd194..4a6dee43d 100644 --- a/docs/number.md +++ b/docs/number.md @@ -168,11 +168,7 @@ Number.isInteger(3e-324) // false // 同样由于精度问题,即使是比`Number.MIN_VALUE`略小的数也会被判为 5e-324。 ``` -因此,在金融、天文等领域的数据精度要求较高、判断值是否整数的情况下,不建议使用`Number.isInteger()`原生函数,请使用以下包含正则的函数进行处理: - -```javascript - -``` +因此,在金融、天文等领域的数据精度要求较高、判断值是否整数的情况下,不建议使用`Number.isInteger()`原生函数,请使用包含正则的函数替代。 ES5 可以通过下面的代码,部署`Number.isInteger()`。 From ef25faa3dc34bf19f8ae36bdb619b9905a640001 Mon Sep 17 00:00:00 2001 From: picc-lu <32639909+picc-lu@users.noreply.github.com> Date: Thu, 4 Jan 2018 23:31:25 +0800 Subject: [PATCH 059/595] =?UTF-8?q?=E4=B8=B4=E6=97=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/number.md | 27 ++++++++++++++++++++------- 1 file changed, 20 insertions(+), 7 deletions(-) diff --git a/docs/number.md b/docs/number.md index 4a6dee43d..faaf21261 100644 --- a/docs/number.md +++ b/docs/number.md @@ -538,17 +538,30 @@ Math.imul(0x7fffffff, 0x7fffffff) // 1 ### Math.fround() -Math.fround 方法返回一个数的单精度浮点数形式。 +`Math.fround` 方法返回一个数的单精度浮点数形式。 + +对于 -2 的 24 次方至 2 的 24 次方(不含两端)的整数,返回结果与参数本身一致。 + +```javascript +Math.fround(0) // 0 +Math.fround(1) // 1 +Math.fround(2 ** 24 - 1) // 16777215 +``` + +单精度浮点数采用 IEEE 754 标准,最多由 24 位二进制位数(1 位隐藏位与 23 位有效位)表达数值,若参数绝对值大于 2 的 24 次方,返回的结果便开始丢失精度。 ```javascript -Math.fround(0) // 0 -Math.fround(1) // 1 -Math.fround(1.337) // 1.3370000123977661 -Math.fround(1.5) // 1.5 -Math.fround(NaN) // NaN +Math.fround(2 ** 24) // 16777216 +Math.fround(2 ** 24 + 1) // 16777216 ``` -对于整数来说,`Math.fround`方法返回结果不会有任何不同,区别主要是那些无法用 64 个二进制位精确表示的小数。这时,`Math.fround`方法会返回最接近这个小数的单精度浮点数。 +`Math.fround` 方法主要将双精度浮点数转为单精度浮点数,第 25 位二进制数尝试往上一位进位,它与它往后的位数全部丢弃。 + +```javascript +Math.fround(1.125) // 1.125 +Math.fround(0.3) // 0.30000001192092896 +Math.fround(0.8)   // 0.800000011920929 +``` 对于没有部署这个方法的环境,可以用下面的代码模拟。 From c122e669dc51154051076421223195ca43855035 Mon Sep 17 00:00:00 2001 From: picc-lu <32639909+picc-lu@users.noreply.github.com> Date: Fri, 5 Jan 2018 11:57:53 +0800 Subject: [PATCH 060/595] Update Math.fround MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 基于 IEEE 754 标准修改 Math.fround 一节的介绍 --- docs/number.md | 26 ++++++++++++++++++++++---- 1 file changed, 22 insertions(+), 4 deletions(-) diff --git a/docs/number.md b/docs/number.md index faaf21261..ee7395894 100644 --- a/docs/number.md +++ b/docs/number.md @@ -543,8 +543,8 @@ Math.imul(0x7fffffff, 0x7fffffff) // 1 对于 -2 的 24 次方至 2 的 24 次方(不含两端)的整数,返回结果与参数本身一致。 ```javascript -Math.fround(0) // 0 -Math.fround(1) // 1 +Math.fround(0) // 0 +Math.fround(1)   // 1 Math.fround(2 ** 24 - 1) // 16777215 ``` @@ -558,9 +558,27 @@ Math.fround(2 ** 24 + 1) // 16777216 `Math.fround` 方法主要将双精度浮点数转为单精度浮点数,第 25 位二进制数尝试往上一位进位,它与它往后的位数全部丢弃。 ```javascript +// 未丢失有效精度 Math.fround(1.125) // 1.125 -Math.fround(0.3) // 0.30000001192092896 -Math.fround(0.8)   // 0.800000011920929 +Math.fround(7.25) // 7.25 + +// 丢失精度 +Math.fround(0.3)   // 0.30000001192092896 +Math.fround(0.7)   // 0.699999988079071 +Math.fround(1.0000000123) // 1 +``` + +对于 NaN 和 Infinity ,此方法返回原值。其它类型而言,`Math.fround` 方法会将其转为数值再返回单精度浮点数。 + +```javascript +Math.fround(NaN) // NaN +Math.fround(Infinity) // Infinity + +Math.fround('5') // 5 +Math.fround(true) // 1 +Math.fround(null) // 0 +Math.fround([]) // 0 +Math.fround({}) // NaN ``` 对于没有部署这个方法的环境,可以用下面的代码模拟。 From efd6a71825aad949ff2115c522ca4eb3b70f5a37 Mon Sep 17 00:00:00 2001 From: picc-lu <32639909+picc-lu@users.noreply.github.com> Date: Fri, 5 Jan 2018 12:11:19 +0800 Subject: [PATCH 061/595] Update Math.fround MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 小修 --- docs/number.md | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/docs/number.md b/docs/number.md index ee7395894..a18ad3086 100644 --- a/docs/number.md +++ b/docs/number.md @@ -147,7 +147,8 @@ Number.isInteger(true) // false ```javascript 3 === 3 + 2e-16 // true Number.isInteger(3.0000000000000002) // true -// 3 的二进制 2 位,2e-16 的二进制最多能表示 51 位,第 3 ~ 53 位全为 0,直到第 55 位才开始出现 1,而这一位被丢弃了,误判为 true +// 3 的二进制 2 位,2e-16 的二进制最多能表示 51 位 +// 第 3 ~ 53 位全为 0,直到第 55 位才开始出现 1,而这一位被丢弃了,误判为 true 3 + 4e-16 === 3 + 6e-16 // true Number.isInteger(3.0000000000000004) // false @@ -155,7 +156,7 @@ Number.isInteger(3.0000000000000006) // false // 第 51 位已为 1,且不会丢精度,所以 JavaScript 判定此数包含小数,返回 false ``` -数值的大小在 -1 与 1 之间(不含两个端点)时,其绝对值小于`Number.MIN_VALUE`即视为 0。 +数值的大小在 -1 与 1 之间(不含两个端点)时,其绝对值小于 `Number.MIN_VALUE` 即视为 0。 ```javascript Number.MIN_VALUE // 5e-324 @@ -165,7 +166,7 @@ Number.isInteger(5e-325) // true 3e-324 === Number.MIN_VALUE // true Number.isInteger(3e-324) // false -// 同样由于精度问题,即使是比`Number.MIN_VALUE`略小的数也会被判为 5e-324。 +// 同样由于精度问题,即使是比 Number.MIN_VALUE 略小的数也会被判为 5e-324。 ``` 因此,在金融、天文等领域的数据精度要求较高、判断值是否整数的情况下,不建议使用`Number.isInteger()`原生函数,请使用包含正则的函数替代。 @@ -543,7 +544,7 @@ Math.imul(0x7fffffff, 0x7fffffff) // 1 对于 -2 的 24 次方至 2 的 24 次方(不含两端)的整数,返回结果与参数本身一致。 ```javascript -Math.fround(0) // 0 +Math.fround(0) // 0 Math.fround(1)   // 1 Math.fround(2 ** 24 - 1) // 16777215 ``` @@ -568,7 +569,7 @@ Math.fround(0.7)   // 0.699999988079071 Math.fround(1.0000000123) // 1 ``` -对于 NaN 和 Infinity ,此方法返回原值。其它类型而言,`Math.fround` 方法会将其转为数值再返回单精度浮点数。 +对于 `NaN` 和 `Infinity`,此方法返回原值。其它类型而言,`Math.fround` 方法会将其转为数值再返回单精度浮点数。 ```javascript Math.fround(NaN) // NaN From 6807435578b83994f8cd276a5e086270e11ff363 Mon Sep 17 00:00:00 2001 From: ruanyf Date: Sat, 6 Jan 2018 14:38:24 +0800 Subject: [PATCH 062/595] docs(Number): edit Number --- docs/number.md | 75 +++++++++++++++++++------------------------------- 1 file changed, 29 insertions(+), 46 deletions(-) diff --git a/docs/number.md b/docs/number.md index a18ad3086..b72fd5226 100644 --- a/docs/number.md +++ b/docs/number.md @@ -132,70 +132,53 @@ Number.parseFloat === parseFloat // true ## Number.isInteger() -`Number.isInteger()`用来判断一个值是否为整数。需要注意的是,在 JavaScript 内部,整数和浮点数采用的是同样的储存方法,所以 3 和 3.0 被视为同一个值。 +`Number.isInteger()`用来判断一个数值是否为整数。 ```javascript Number.isInteger(25) // true -Number.isInteger(25.0) // true Number.isInteger(25.1) // false -Number.isInteger("15") // false -Number.isInteger(true) // false ``` -注意,由于 JavaScript 浮点数采用的是 IEEE 754 标准,表示数值时最多只能存储 53 位二进制位数(1 位隐藏位与 52 位有效位)。超出位数上限时,第 54 位会尝试是否往第 53 位进位(0 不进位,1 进位),它和它往后的位数一概丢弃,这种情况下可能会导致误判。 +JavaScript 内部,整数和浮点数采用的是同样的储存方法,所以 25 和 25.0 被视为同一个值。 ```javascript -3 === 3 + 2e-16 // true -Number.isInteger(3.0000000000000002) // true -// 3 的二进制 2 位,2e-16 的二进制最多能表示 51 位 -// 第 3 ~ 53 位全为 0,直到第 55 位才开始出现 1,而这一位被丢弃了,误判为 true - -3 + 4e-16 === 3 + 6e-16 // true -Number.isInteger(3.0000000000000004) // false -Number.isInteger(3.0000000000000006) // false -// 第 51 位已为 1,且不会丢精度,所以 JavaScript 判定此数包含小数,返回 false +Number.isInteger(25) // true +Number.isInteger(25.0) // true ``` -数值的大小在 -1 与 1 之间(不含两个端点)时,其绝对值小于 `Number.MIN_VALUE` 即视为 0。 +如果参数不是数值,`Number.isInteger`返回`false`。 ```javascript -Number.MIN_VALUE // 5e-324 -5e-325 === 0 // true -Number.isInteger(Number.MIN_VALUE) // false -Number.isInteger(5e-325) // true +Number.isInteger() // false +Number.isInteger(null) // false +Number.isInteger('15') // false +Number.isInteger(true) // false +``` -3e-324 === Number.MIN_VALUE // true -Number.isInteger(3e-324) // false -// 同样由于精度问题,即使是比 Number.MIN_VALUE 略小的数也会被判为 5e-324。 +注意,由于 JavaScript 采用 IEEE 754 标准,数值存储为64位双精度格式,数值精度最多可以达到 53 个二进制位(1 个隐藏位与 52 个有效位)。如果数值的精度超过这个限度,第54位及后面的位就会被丢弃,这种情况下,`Number.isInteger`可能会误判。 + +```javascript +Number.isInteger(3.0000000000000002) // true ``` -因此,在金融、天文等领域的数据精度要求较高、判断值是否整数的情况下,不建议使用`Number.isInteger()`原生函数,请使用包含正则的函数替代。 +上面代码中,`Number.isInteger`的参数明明不是整数,但是会返回`true`。原因就是这个小数的精度达到了小数点后16个十进制位,转成二进制位超过了53个二进制位,导致最后的那个`2`被丢弃了。 -ES5 可以通过下面的代码,部署`Number.isInteger()`。 +类似的情况还有,如果一个数值的绝对值小于`Number.MIN_VALUE`(5E-324),即小于 JavaScript 能够分辨的最小值,会被自动转为 0。这时,`Number.isInteger`也会误判。 ```javascript -(function (global) { - var floor = Math.floor, - isFinite = global.isFinite; - - Object.defineProperty(Number, 'isInteger', { - value: function isInteger(value) { - return typeof value === 'number' && - isFinite(value) && - floor(value) === value; - }, - configurable: true, - enumerable: false, - writable: true - }); -})(this); +Number.isInteger(5E-324) // false +Number.isInteger(5E-325) // true ``` +上面代码中,`5E-325`由于值太小,会被自动转为0,因此返回`true`。 + +总之,如果对数据精度的要求较高,不建议使用`Number.isInteger()`判断一个数值是否为整数。 + ## Number.EPSILON ES6 在`Number`对象上面,新增一个极小的常量`Number.EPSILON`。根据规格,它表示 1 与大于 1 的最小浮点数之间的差。 -对于 64 位浮点数来说,大于 1 的最小浮点数相当于二进制的`1.00..001`,小数点后面有连续 51 个零。这个值减去 1 之后,就等于 2 的-52 次方。 +对于 64 位浮点数来说,大于 1 的最小浮点数相当于二进制的`1.00..001`,小数点后面有连续 51 个零。这个值减去 1 之后,就等于 2 的 -52 次方。 ```javascript Number.EPSILON === Math.pow(2, -52) @@ -539,9 +522,9 @@ Math.imul(0x7fffffff, 0x7fffffff) // 1 ### Math.fround() -`Math.fround` 方法返回一个数的单精度浮点数形式。 +`Math.fround`方法返回一个数的32位单精度浮点数形式。 -对于 -2 的 24 次方至 2 的 24 次方(不含两端)的整数,返回结果与参数本身一致。 +对于32位单精度格式来说,数值精度是24个二进制位(1 位隐藏位与 23 位有效位),所以对于 -224 至 224 之间的整数(不含两个端点),返回结果与参数本身一致。 ```javascript Math.fround(0) // 0 @@ -549,14 +532,14 @@ Math.fround(1)   // 1 Math.fround(2 ** 24 - 1) // 16777215 ``` -单精度浮点数采用 IEEE 754 标准,最多由 24 位二进制位数(1 位隐藏位与 23 位有效位)表达数值,若参数绝对值大于 2 的 24 次方,返回的结果便开始丢失精度。 +如果参数的绝对值大于 224,返回的结果便开始丢失精度。 ```javascript Math.fround(2 ** 24) // 16777216 Math.fround(2 ** 24 + 1) // 16777216 ``` -`Math.fround` 方法主要将双精度浮点数转为单精度浮点数,第 25 位二进制数尝试往上一位进位,它与它往后的位数全部丢弃。 +`Math.fround`方法的主要作用,是将64位双精度浮点数转为32位单精度浮点数。如果小数的精度超过24个二进制位,返回值就会不同于原值,否则返回值不变(即与64位双精度值一致)。 ```javascript // 未丢失有效精度 @@ -569,7 +552,7 @@ Math.fround(0.7)   // 0.699999988079071 Math.fround(1.0000000123) // 1 ``` -对于 `NaN` 和 `Infinity`,此方法返回原值。其它类型而言,`Math.fround` 方法会将其转为数值再返回单精度浮点数。 +对于 `NaN` 和 `Infinity`,此方法返回原值。对于其它类型的非数值,`Math.fround` 方法会先将其转为数值,再返回单精度浮点数。 ```javascript Math.fround(NaN) // NaN @@ -585,7 +568,7 @@ Math.fround({}) // NaN 对于没有部署这个方法的环境,可以用下面的代码模拟。 ```javascript -Math.fround = Math.fround || function(x) { +Math.fround = Math.fround || function (x) { return new Float32Array([x])[0]; }; ``` From 796501b2648a3b143f75eba1a05c68820fdd6bd0 Mon Sep 17 00:00:00 2001 From: ruanyf Date: Sat, 6 Jan 2018 16:34:23 +0800 Subject: [PATCH 063/595] docs(function): fix typo --- docs/function.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/function.md b/docs/function.md index 2eaa04c65..5fa0d97c3 100644 --- a/docs/function.md +++ b/docs/function.md @@ -944,7 +944,7 @@ let log = ::console.log; var log = console.log.bind(console); ``` -双冒号运算符的运算结果,还是一个对象,因此可以采用链式写法。 +双冒号运算符的运算结果,还是一个函数,因此可以采用链式写法。 ```javascript // 例一 From 40707121411f1b1c0225ce5962929fd39d85e2a9 Mon Sep 17 00:00:00 2001 From: LiMengKe <752140235@qq.com> Date: Sun, 7 Jan 2018 23:34:14 +0800 Subject: [PATCH 064/595] Update decorator.md ${name} is wrapped with "" --- docs/decorator.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/decorator.md b/docs/decorator.md index 484363439..7791ace26 100644 --- a/docs/decorator.md +++ b/docs/decorator.md @@ -200,7 +200,7 @@ function log(target, name, descriptor) { var oldValue = descriptor.value; descriptor.value = function() { - console.log(`Calling "${name}" with`, arguments); + console.log(`Calling ${name} with`, arguments); return oldValue.apply(null, arguments); }; From 71bebc50117130493c2d756ebb3285880f427f18 Mon Sep 17 00:00:00 2001 From: picc-lu <32639909+picc-lu@users.noreply.github.com> Date: Mon, 8 Jan 2018 21:16:45 +0800 Subject: [PATCH 065/595] Update Array MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 1. 修改copyWithin方法中target参数的描述 2. 添加一个find方法一节中的例子 --- docs/array.md | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/docs/array.md b/docs/array.md index ebb5e9609..2246ec7ab 100644 --- a/docs/array.md +++ b/docs/array.md @@ -515,7 +515,7 @@ Array.prototype.copyWithin(target, start = 0, end = this.length) 它接受三个参数。 -- target(必需):从该位置开始替换数据。 +- target(必需):从该位置开始替换数据。如果为负值,表示倒数。 - start(可选):从该位置开始读取数据,默认为 0。如果为负值,表示倒数。 - end(可选):到该位置前停止读取数据,默认等于数组长度。如果为负值,表示倒数。 @@ -583,6 +583,16 @@ i32a.copyWithin(0, 2); 这两个方法都可以接受第二个参数,用来绑定回调函数的`this`对象。 +```javascript +function f(v){ + return v > this.age; +} +let person = {name: 'John', age: 20}; +[10, 12, 26, 15].find(f, person); // 26 +``` + +上面的代码中,`find`函数接收了第二个参数`person`对象,回调函数中的`this`对象指向`person`对象。 + 另外,这两个方法都可以发现`NaN`,弥补了数组的`indexOf`方法的不足。 ```javascript From f847132b3c8f9856022ee36699064c2cde15095b Mon Sep 17 00:00:00 2001 From: ruanyf Date: Thu, 11 Jan 2018 11:25:46 +0800 Subject: [PATCH 066/595] docs(iterator): fix return() #585 --- docs/iterator.md | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/docs/iterator.md b/docs/iterator.md index 39fc7a20e..a05753f63 100644 --- a/docs/iterator.md +++ b/docs/iterator.md @@ -480,12 +480,16 @@ for (let x of obj) { ```javascript function readLinesSync(file) { return { - next() { - return { done: false }; - }, - return() { - file.close(); - return { done: true }; + [Symbol.iterator]() { + return { + next() { + return { done: false }; + }, + return() { + file.close(); + return { done: true }; + } + }; }, }; } From bf1914497b4d15032c0ea5367e08e98f989515e4 Mon Sep 17 00:00:00 2001 From: ruanyf Date: Thu, 11 Jan 2018 14:27:31 +0800 Subject: [PATCH 067/595] docs(module): fix import script suffix --- docs/module.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/module.md b/docs/module.md index c27fa18ef..78d5ca2c1 100644 --- a/docs/module.md +++ b/docs/module.md @@ -190,7 +190,7 @@ foo() ```javascript // main.js -import {firstName, lastName, year} from './profile'; +import {firstName, lastName, year} from './profile.js'; function setName(element) { element.textContent = firstName + ' ' + lastName; @@ -202,7 +202,7 @@ function setName(element) { 如果想为输入的变量重新取一个名字,`import`命令要使用`as`关键字,将输入的变量重命名。 ```javascript -import { lastName as surname } from './profile'; +import { lastName as surname } from './profile.js'; ``` `import`后面的`from`指定模块文件的位置,可以是相对路径,也可以是绝对路径,`.js`后缀可以省略。如果只是模块名,不带有路径,那么必须有配置文件,告诉 JavaScript 引擎该模块的位置。 From 042150930dc0925d497fb3cb68f5e919c0d10ebd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=B8=89=E5=92=B2=E4=BA=9A=E5=AD=90?= Date: Sun, 14 Jan 2018 00:31:35 +0800 Subject: [PATCH 068/595] Update generator.md MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 统一代码风格 --- docs/generator.md | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/docs/generator.md b/docs/generator.md index d8bc69d95..ae00285fb 100644 --- a/docs/generator.md +++ b/docs/generator.md @@ -313,7 +313,7 @@ wrapped().next('hello!') `for...of`循环可以自动遍历 Generator 函数时生成的`Iterator`对象,且此时不再需要调用`next`方法。 ```javascript -function *foo() { +function* foo() { yield 1; yield 2; yield 3; @@ -912,16 +912,16 @@ read.next().value // "h" 如果被代理的 Generator 函数有`return`语句,那么就可以向代理它的 Generator 函数返回数据。 ```javascript -function *foo() { +function* foo() { yield 2; yield 3; return "foo"; } -function *bar() { +function* bar() { yield 1; - var v = yield *foo(); - console.log( "v: " + v ); + var v = yield* foo(); + console.log("v: " + v); yield 4; } @@ -1225,7 +1225,7 @@ JavaScript 代码运行时,会产生一个全局的上下文环境(context Generator 函数不是这样,它执行产生的上下文环境,一旦遇到`yield`命令,就会暂时退出堆栈,但是并不消失,里面的所有变量和对象会冻结在当前状态。等到对它执行`next`命令时,这个上下文环境又会重新加入调用栈,冻结的变量和对象恢复执行。 ```javascript -function *gen() { +function* gen() { yield 1; return 2; } @@ -1371,7 +1371,7 @@ function scheduler(task) { ```javascript let steps = [step1Func, step2Func, step3Func]; -function *iterateSteps(steps){ +function* iterateSteps(steps){ for (var i=0; i< steps.length; i++){ var step = steps[i]; yield step(); @@ -1467,7 +1467,7 @@ gen.next().done // true Generator 可以看作是数据结构,更确切地说,可以看作是一个数组结构,因为 Generator 函数可以返回一系列的值,这意味着它可以对任意表达式,提供类似数组的接口。 ```javascript -function *doStuff() { +function* doStuff() { yield fs.readFile.bind(null, 'hello.txt'); yield fs.readFile.bind(null, 'world.txt'); yield fs.readFile.bind(null, 'and-such.txt'); From 2fe9716a0987bda762da62d086d011f89f63ae5a Mon Sep 17 00:00:00 2001 From: ruanyf Date: Sun, 14 Jan 2018 22:07:05 +0800 Subject: [PATCH 069/595] docs(spec): edit glossary --- docs/reference.md | 1 + docs/spec.md | 85 ++++++++++++++++++++++++++++++++++++++++++++++- docs/symbol.md | 4 +-- 3 files changed, 87 insertions(+), 3 deletions(-) diff --git a/docs/reference.md b/docs/reference.md index a60a38846..3c8feb6ef 100644 --- a/docs/reference.md +++ b/docs/reference.md @@ -36,6 +36,7 @@ - Jani Hartikainen, [ES6: What are the benefits of the new features in practice?](http://codeutopia.net/blog/2015/01/06/es6-what-are-the-benefits-of-the-new-features-in-practice/) - kangax, [Javascript quiz. ES6 edition](http://perfectionkills.com/javascript-quiz-es6/): ES6 小测试 - Jeremy Fairbank, [HTML5DevConf ES7 and Beyond!](https://speakerdeck.com/jfairbank/html5devconf-es7-and-beyond): ES7 新增语法点介绍 +- Timothy Gu, [How to Read the ECMAScript Specification](https://timothygu.me/es-howto/): 如何读懂 ES6 规格 ## let 和 const diff --git a/docs/spec.md b/docs/spec.md index d3a258fa8..2c0fea01e 100644 --- a/docs/spec.md +++ b/docs/spec.md @@ -16,10 +16,93 @@ ECMAScript 6 的规格,可以在 ECMA 国际标准组织的官方网站([www ECMAScript 6 规格的 26 章之中,第 1 章到第 3 章是对文件本身的介绍,与语言关系不大。第 4 章是对这门语言总体设计的描述,有兴趣的读者可以读一下。第 5 章到第 8 章是语言宏观层面的描述。第 5 章是规格的名词解释和写法的介绍,第 6 章介绍数据类型,第 7 章介绍语言内部用到的抽象操作,第 8 章介绍代码如何运行。第 9 章到第 26 章介绍具体的语法。 -对于一般用户来说,除了第 4 章,其他章节都涉及某一方面的细节,不用通读,只要在用到的时候,查阅相关章节即可。下面通过一些例子,介绍如何使用这份规格。 +对于一般用户来说,除了第 4 章,其他章节都涉及某一方面的细节,不用通读,只要在用到的时候,查阅相关章节即可。 + +## 术语 + +ES6 规格使用了一些专门的术语,了解这些术语,可以帮助你读懂规格。本节介绍其中的几个。 + +### 抽象操作 + +所谓”抽象操作“(abstract operations)就是引擎的一些内部方法,外部不能调用。规格定义了一系列的抽象操作,规定了它们的行为,留给各种引擎自己去实现。 + +举例来说,`Boolean(value)`的算法,第一步是这样的。 + +> 1. Let b be ToBoolean(value). + +这里的`ToBoolean`就是一个抽象操作,是引擎内部求出布尔值的算法。 + +许多函数的算法都会多次用到同样的步骤,所以 ES6 规格将它们抽出来,定义成”抽象操作“,方便描述。 + +### Record 和 field + +ES6 规格将键值对(key-value map)的数据结构称为 Record,其中的每一组键值对称为 field。这就是说,一个 Record 由多个 field 组成,而每个 field 都包含一个键名(key)和一个键值(value)。 + +### [[Notation]] + +ES6 规格大量使用`[[Notation]]`这种书写法,比如`[[Value]]`、`[[Writable]]`、`[[Get]]`、`[[Set]]`等等。它用来指代 field 的键名。 + +举例来说,`obj`是一个 Record,它有一个`Prototype`属性。ES6 规格不会写`obj.Prototype`,而是写`obj.[[Prototype]]`。一般来说,使用`[[Notation]]`这种书写法的属性,都是对象的内部属性。 + +所有的 JavaScript 函数都有一个内部属性`[[Call]]`,用来运行该函数。 + +```javascript +F.[[Call]](V, argumentsList) +``` + +上面代码中,`F`是一个函数对象,`[[Call]]`是它的内部方法,`F.[[call]]()`表示运行该函数,`V`表示`[[Call]]`运行时`this`的值,`argumentsList`则是调用时传入函数的参数。 + +### Completion Record + +每一个语句都会返回一个 Completion Record,表示运行结果。每个 Completion Record 有一个`[[Type]]`属性,表示运行结果的类型。 + +`[[Type]]`属性有五种可能的值。 + +- normal +- return +- throw +- break +- continue + +如果`[[Type]]`的值是`normal`,就称为 normal completion,表示运行正常。其他的值,都称为 abrupt completion。其中,开发者只需要关注`[[Type]]`为`throw`的情况,即运行出错;`break`、`continue`、`return`这三个值都只出现在特定场景,可以不用考虑。 + +## 抽象操作的标准流程 + +抽象操作的运行流程,一般是下面这样。 + +> 1. Let resultCompletionRecord be AbstractOp(). +> 1. If resultCompletionRecord is an abrupt completion, return resultCompletionRecord. +> 1. Let result be resultCompletionRecord.[[Value]]. +> 1. return result. + +上面的第一步是调用抽象操作`AbstractOp()`,得到`resultCompletionRecord`,这是一个 Completion Record。第二步,如果这个 Record 属于 abrupt completion,就将`resultCompletionRecord`返回给用户。如果此处没有返回,就表示运行结果正常,所得的值存放在`resultCompletionRecord.[[Value]]`属性。第三步,将这个值记为`result`。第四步,将`result`返回给用户。 + +ES6 规格将这个标准流程,使用简写的方式表达。 + +> 1. Let result be AbstractOp(). +> 1. ReturnIfAbrupt(result). +> 1. return result. + +这个简写方式里面的`ReturnIfAbrupt(result)`,就代表了上面的第二步和第三步,即如果有报错,就返回错误,否则取出值。 + +甚至还有进一步的简写格式。 + +> 1. Let result be ? AbstractOp(). +> 1. return result. + +上面流程的`?`,就代表`AbstractOp()`可能会报错。一旦报错,就返回错误,否则取出值。 + +除了`?`,ES 6 规格还使用另一个简写符号`!`。 + +> 1. Let result be ! AbstractOp(). +> 1. return result. + +上面流程的`!`,代表`AbstractOp()`不会报错,返回的一定是 normal completion,总是可以取出值。 ## 相等运算符 +下面通过一些例子,介绍如何使用这份规格。 + 相等运算符(`==`)是一个很让人头痛的运算符,它的语法行为多变,不符合直觉。这个小节就看看规格怎么规定它的行为。 请看下面这个表达式,请问它的值是多少。 diff --git a/docs/symbol.md b/docs/symbol.md index f88817299..66db7062a 100644 --- a/docs/symbol.md +++ b/docs/symbol.md @@ -563,7 +563,7 @@ a2[1] = 6; ### Symbol.species -对象的`Symbol.species`属性,指向一个构造函数。创建造衍生对象时,会使用该属性。 +对象的`Symbol.species`属性,指向一个构造函数。创建衍生对象时,会使用该属性。 ```javascript class MyArray extends Array { @@ -583,7 +583,7 @@ class MyArray extends Array { } ``` -上面代码中,由于定义了`Symbol.species`属性,创建衍生对象时就会使用这个属性返回的的函数,作为构造函数。这个例子也说明,定义`Symbol.species`属性要采用`get`读取器。默认的`Symbol.species`属性等同于下面的写法。 +上面代码中,由于定义了`Symbol.species`属性,创建衍生对象时就会使用这个属性返回的函数,作为构造函数。这个例子也说明,定义`Symbol.species`属性要采用`get`读取器。默认的`Symbol.species`属性等同于下面的写法。 ```javascript static get [Symbol.species]() { From 26bb54f20bb2e20432610c0db111ca2b27da480f Mon Sep 17 00:00:00 2001 From: ruanyf Date: Sun, 14 Jan 2018 22:10:47 +0800 Subject: [PATCH 070/595] docs(spec): edit glossary --- docs/spec.md | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/docs/spec.md b/docs/spec.md index 2c0fea01e..bebe91753 100644 --- a/docs/spec.md +++ b/docs/spec.md @@ -28,7 +28,7 @@ ES6 规格使用了一些专门的术语,了解这些术语,可以帮助你 举例来说,`Boolean(value)`的算法,第一步是这样的。 -> 1. Let b be ToBoolean(value). +> 1. Let `b` be `ToBoolean(value)`. 这里的`ToBoolean`就是一个抽象操作,是引擎内部求出布尔值的算法。 @@ -79,23 +79,23 @@ F.[[Call]](V, argumentsList) ES6 规格将这个标准流程,使用简写的方式表达。 -> 1. Let result be AbstractOp(). -> 1. ReturnIfAbrupt(result). -> 1. return result. +> 1. Let `result` be `AbstractOp()`. +> 1. `ReturnIfAbrupt(result)`. +> 1. return `result`. 这个简写方式里面的`ReturnIfAbrupt(result)`,就代表了上面的第二步和第三步,即如果有报错,就返回错误,否则取出值。 甚至还有进一步的简写格式。 -> 1. Let result be ? AbstractOp(). -> 1. return result. +> 1. Let `result` be `? AbstractOp()`. +> 1. return `result`. 上面流程的`?`,就代表`AbstractOp()`可能会报错。一旦报错,就返回错误,否则取出值。 除了`?`,ES 6 规格还使用另一个简写符号`!`。 -> 1. Let result be ! AbstractOp(). -> 1. return result. +> 1. Let `result` be `! AbstractOp()`. +> 1. return `result`. 上面流程的`!`,代表`AbstractOp()`不会报错,返回的一定是 normal completion,总是可以取出值。 From c568f107106259b41b5cece19ef20562a1235b90 Mon Sep 17 00:00:00 2001 From: ruanyf Date: Sun, 14 Jan 2018 22:12:24 +0800 Subject: [PATCH 071/595] docs(spec): edit glossary --- docs/spec.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/docs/spec.md b/docs/spec.md index bebe91753..8a375cfcf 100644 --- a/docs/spec.md +++ b/docs/spec.md @@ -70,10 +70,10 @@ F.[[Call]](V, argumentsList) 抽象操作的运行流程,一般是下面这样。 -> 1. Let resultCompletionRecord be AbstractOp(). -> 1. If resultCompletionRecord is an abrupt completion, return resultCompletionRecord. -> 1. Let result be resultCompletionRecord.[[Value]]. -> 1. return result. +> 1. Let `resultCompletionRecord` be `AbstractOp()`. +> 1. If `resultCompletionRecord` is an abrupt completion, return `resultCompletionRecord`. +> 1. Let `result` be `resultCompletionRecord.[[Value]]`. +> 1. return `result`. 上面的第一步是调用抽象操作`AbstractOp()`,得到`resultCompletionRecord`,这是一个 Completion Record。第二步,如果这个 Record 属于 abrupt completion,就将`resultCompletionRecord`返回给用户。如果此处没有返回,就表示运行结果正常,所得的值存放在`resultCompletionRecord.[[Value]]`属性。第三步,将这个值记为`result`。第四步,将`result`返回给用户。 From 67776aa4a4e0d55904671db3469ffc4253c1a42b Mon Sep 17 00:00:00 2001 From: ruanyf Date: Tue, 16 Jan 2018 13:14:59 +0800 Subject: [PATCH 072/595] docs: edit number --- docs/number.md | 40 +++++----------------------------------- 1 file changed, 5 insertions(+), 35 deletions(-) diff --git a/docs/number.md b/docs/number.md index b72fd5226..3e477f6ed 100644 --- a/docs/number.md +++ b/docs/number.md @@ -35,7 +35,7 @@ Number('0o10') // 8 ES6 在`Number`对象上,新提供了`Number.isFinite()`和`Number.isNaN()`两个方法。 -`Number.isFinite()`用来检查一个数值是否为有限的(finite)。 +`Number.isFinite()`用来检查一个数值是否为有限的(finite),即不是`Infinity`。 ```javascript Number.isFinite(15); // true @@ -48,22 +48,7 @@ Number.isFinite('15'); // false Number.isFinite(true); // false ``` -ES5 可以通过下面的代码,部署`Number.isFinite`方法。 - -```javascript -(function (global) { - var global_isFinite = global.isFinite; - - Object.defineProperty(Number, 'isFinite', { - value: function isFinite(value) { - return typeof value === 'number' && global_isFinite(value); - }, - configurable: true, - enumerable: false, - writable: true - }); -})(this); -``` +注意,如果参数类型不是数值,`Number.isFinite`一律返回`false`。 `Number.isNaN()`用来检查一个值是否为`NaN`。 @@ -73,26 +58,11 @@ Number.isNaN(15) // false Number.isNaN('15') // false Number.isNaN(true) // false Number.isNaN(9/NaN) // true -Number.isNaN('true'/0) // true -Number.isNaN('true'/'true') // true +Number.isNaN('true' / 0) // true +Number.isNaN('true' / 'true') // true ``` -ES5 通过下面的代码,部署`Number.isNaN()`。 - -```javascript -(function (global) { - var global_isNaN = global.isNaN; - - Object.defineProperty(Number, 'isNaN', { - value: function isNaN(value) { - return typeof value === 'number' && global_isNaN(value); - }, - configurable: true, - enumerable: false, - writable: true - }); -})(this); -``` +注意,如果参数类型不是数值,`Number.isNaN`一律返回`false`。 它们与传统的全局方法`isFinite()`和`isNaN()`的区别在于,传统方法先调用`Number()`将非数值的值转为数值,再进行判断,而这两个新方法只对数值有效,`Number.isFinite()`对于非数值一律返回`false`, `Number.isNaN()`只有对于`NaN`才返回`true`,非`NaN`一律返回`false`。 From 993d82bfa0229071a386f63605cb4cbf38f4311a Mon Sep 17 00:00:00 2001 From: ruanyf Date: Sun, 21 Jan 2018 22:18:07 +0800 Subject: [PATCH 073/595] docs(arraybuffer): edit arraybuffer --- docs/arraybuffer.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/arraybuffer.md b/docs/arraybuffer.md index f0782612b..bc0cfda40 100644 --- a/docs/arraybuffer.md +++ b/docs/arraybuffer.md @@ -1125,7 +1125,7 @@ Atomics.add(ia, 112, 1); // 正确 `store()`方法用来向共享内存写入数据,`load()`方法用来从共享内存读出数据。比起直接的读写操作,它们的好处是保证了读写操作的原子性。 -此外,它们还用来解决一个问题:多个线程使用共享线程的某个位置作为开关(flag),一旦该位置的值变了,就执行特定操作。这时,必须保证该位置的赋值操作,一定是在它前面的所有可能会改写内存的操作结束后执行;而该位置的取值操作,一定是在它后面所有可能会读取该位置的操作开始之前执行。`store`方法和`load`方法就能做到这一点,编译器不会为了优化,而打乱机器指令的执行顺序。 +此外,它们还用来解决一个问题:多个线程使用共享内存的某个位置作为开关(flag),一旦该位置的值变了,就执行特定操作。这时,必须保证该位置的赋值操作,一定是在它前面的所有可能会改写内存的操作结束后执行;而该位置的取值操作,一定是在它后面所有可能会读取该位置的操作开始之前执行。`store`方法和`load`方法就能做到这一点,编译器不会为了优化,而打乱机器指令的执行顺序。 ```javascript Atomics.load(array, index) From 0ce61292713789f0d485cd218cb9a0442c0e7168 Mon Sep 17 00:00:00 2001 From: ruanyf Date: Mon, 22 Jan 2018 17:21:04 +0800 Subject: [PATCH 074/595] docs(intro): edit intro --- docs/intro.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/intro.md b/docs/intro.md index 935b1ca83..434f98585 100644 --- a/docs/intro.md +++ b/docs/intro.md @@ -6,7 +6,7 @@ ECMAScript 6.0(以下简称 ES6)是 JavaScript 语言的下一代标准, 一个常见的问题是,ECMAScript 和 JavaScript 到底是什么关系? -要讲清楚这个问题,需要回顾历史。1996 年 11 月,JavaScript 的创造者 Netscape 公司,决定将 JavaScript 提交给国际标准化组织 ECMA,希望这种语言能够成为国际标准。次年,ECMA 发布 262 号标准文件(ECMA-262)的第一版,规定了浏览器脚本语言的标准,并将这种语言称为 ECMAScript,这个版本就是 1.0 版。 +要讲清楚这个问题,需要回顾历史。1996 年 11 月,JavaScript 的创造者 Netscape 公司,决定将 JavaScript 提交给标准化组织 ECMA,希望这种语言能够成为国际标准。次年,ECMA 发布 262 号标准文件(ECMA-262)的第一版,规定了浏览器脚本语言的标准,并将这种语言称为 ECMAScript,这个版本就是 1.0 版。 该标准从一开始就是针对 JavaScript 语言制定的,但是之所以不叫 JavaScript,有两个原因。一是商标,Java 是 Sun 公司的商标,根据授权协议,只有 Netscape 公司可以合法地使用 JavaScript 这个名字,且 JavaScript 本身也已经被 Netscape 公司注册为商标。二是想体现这门语言的制定者是 ECMA,不是 Netscape,这样有利于保证这门语言的开放性和中立性。 From ee982c4314fcdb1ba3143e471898f8e98c6b7670 Mon Sep 17 00:00:00 2001 From: ruanyf Date: Wed, 24 Jan 2018 15:08:15 +0800 Subject: [PATCH 075/595] docs(string): edit String.raw --- docs/string.md | 36 ++++++++++++++++++------------------ 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/docs/string.md b/docs/string.md index 3686d33fc..2101f4178 100644 --- a/docs/string.md +++ b/docs/string.md @@ -865,24 +865,36 @@ ES6 还为原生的 String 对象,提供了一个`raw`方法。 ```javascript String.raw`Hi\n${2+3}!`; -// "Hi\\n5!" +// 返回 "Hi\\n5!" String.raw`Hi\u000A!`; -// 'Hi\\u000A!' +// 返回 "Hi\\u000A!" ``` -如果原字符串的斜杠已经转义,那么`String.raw`不会做任何处理。 +如果原字符串的斜杠已经转义,那么`String.raw`会进行再次转义。 ```javascript String.raw`Hi\\n` -// "Hi\\n" +// 返回 "Hi\\\\n" ``` -`String.raw`的代码基本如下。 +`String.raw`方法可以作为处理模板字符串的基本方法,它会将所有变量替换,而且对斜杠进行转义,方便下一步作为字符串来使用。 + +`String.raw`方法也可以作为正常的函数使用。这时,它的第一个参数,应该是一个具有`raw`属性的对象,且`raw`属性的值应该是一个数组。 + +```javascript +String.raw({ raw: 'test' }, 0, 1, 2); +// 't0e1s2t' + +// 等同于 +String.raw({ raw: ['t','e','s','t'] }, 0, 1, 2); +``` + +作为函数,`String.raw`的代码实现基本如下。 ```javascript String.raw = function (strings, ...values) { - let output = ""; + let output = ''; let index; for (index = 0; index < values.length; index++) { output += strings.raw[index] + values[index]; @@ -893,18 +905,6 @@ String.raw = function (strings, ...values) { } ``` -`String.raw`方法可以作为处理模板字符串的基本方法,它会将所有变量替换,而且对斜杠进行转义,方便下一步作为字符串来使用。 - -`String.raw`方法也可以作为正常的函数使用。这时,它的第一个参数,应该是一个具有`raw`属性的对象,且`raw`属性的值应该是一个数组。 - -```javascript -String.raw({ raw: 'test' }, 0, 1, 2); -// 't0e1s2t' - -// 等同于 -String.raw({ raw: ['t','e','s','t'] }, 0, 1, 2); -``` - ## 模板字符串的限制 前面提到标签模板里面,可以内嵌其他语言。但是,模板字符串默认会将字符串转义,导致无法嵌入其他语言。 From 6097cc302f939c0fd2b53e0636a8957740528488 Mon Sep 17 00:00:00 2001 From: ruanyf Date: Thu, 25 Jan 2018 15:54:17 +0800 Subject: [PATCH 076/595] docs(regex): edit regex --- docs/reference.md | 1 + docs/regex.md | 79 +++++++++++++++++++++++++++++++++++++++++------ docs/string.md | 4 +++ 3 files changed, 74 insertions(+), 10 deletions(-) diff --git a/docs/reference.md b/docs/reference.md index 3c8feb6ef..795c61ee2 100644 --- a/docs/reference.md +++ b/docs/reference.md @@ -67,6 +67,7 @@ - Axel Rauschmayer, [New regular expression features in ECMAScript 6](http://www.2ality.com/2015/07/regexp-es6.html):ES6 正则特性的详细介绍 - Yang Guo, [RegExp lookbehind assertions](http://v8project.blogspot.jp/2016/02/regexp-lookbehind-assertions.html):介绍后行断言 - Axel Rauschmayer, [ES proposal: RegExp named capture groups](http://2ality.com/2017/05/regexp-named-capture-groups.html): 具名组匹配的介绍 +- Mathias Bynens, [ECMAScript regular expressions are getting better!](https://mathiasbynens.be/notes/es-regexp-proposals): 介绍 ES2018 添加的多项正则语法 ## 数值 diff --git a/docs/regex.md b/docs/regex.md index 11d238f61..ddc43638b 100644 --- a/docs/regex.md +++ b/docs/regex.md @@ -291,9 +291,9 @@ ES6 为正则表达式新增了`flags`属性,会返回正则表达式的修饰 ## s 修饰符:dotAll 模式 -正则表达式中,点(`.`)是一个特殊字符,代表任意的单个字符,但是行终止符(line terminator character)除外。 +正则表达式中,点(`.`)是一个特殊字符,代表任意的单个字符,但是有两个例外。一个是四个字节的 UTF-16 字符,这个可以用`u`修饰符解决;另一个是行终止符(line terminator character)。 -以下四个字符属于”行终止符“。 +所谓行终止符,就是该字符表示一行的终结。以下四个字符属于”行终止符“。 - U+000A 换行符(`\n`) - U+000D 回车符(`\r`) @@ -314,7 +314,7 @@ ES6 为正则表达式新增了`flags`属性,会返回正则表达式的修饰 // true ``` -这种解决方案毕竟不太符合直觉,所以现在有一个[提案](https://github.com/mathiasbynens/es-regexp-dotall-flag),引入`/s`修饰符,使得`.`可以匹配任意单个字符。 +这种解决方案毕竟不太符合直觉,ES2018 [引入](https://github.com/tc39/proposal-regexp-dotall-flag)`s`修饰符,使得`.`可以匹配任意单个字符。 ```javascript /foo.bar/s.test('foo\nbar') // true @@ -336,7 +336,7 @@ re.flags // 's' ## 后行断言 -JavaScript 语言的正则表达式,只支持先行断言(lookahead)和先行否定断言(negative lookahead),不支持后行断言(lookbehind)和后行否定断言(negative lookbehind)。目前,有一个[提案](https://github.com/goyakin/es-regexp-lookbehind),引入后行断言,V8 引擎 4.9 版已经支持。 +JavaScript 语言的正则表达式,只支持先行断言(lookahead)和先行否定断言(negative lookahead),不支持后行断言(lookbehind)和后行否定断言(negative lookbehind)。ES2018 引入[后行断言](https://github.com/tc39/proposal-regexp-lookbehind),V8 引擎 4.9 版(Chrome 62)已经支持。 ”先行断言“指的是,`x`只有在`y`前面才匹配,必须写成`/x(?=y)/`。比如,只匹配百分号之前的数字,要写成`/\d+(?=%)/`。”先行否定断言“指的是,`x`只有不在`y`前面才匹配,必须写成`/x(?!y)/`。比如,只匹配不在百分号之前的数字,要写成`/\d+(?!%)/`。 @@ -375,9 +375,9 @@ const RE_DOLLAR_PREFIX = /(?<=\$)foo/g; /^(\d+)(\d+)$/.exec('1053') // ["1053", "105", "3"] ``` -上面代码中,需要捕捉两个组匹配。没有"后行断言"时,第一个括号是贪婪模式,第二个括号只能捕获一个字符,所以结果是`105`和`3`。而"后行断言"时,由于执行顺序是从右到左,第二个括号是贪婪模式,第一个括号只能捕获一个字符,所以结果是`1`和`053`。 +上面代码中,需要捕捉两个组匹配。没有“后行断言”时,第一个括号是贪婪模式,第二个括号只能捕获一个字符,所以结果是`105`和`3`。而“后行断言”时,由于执行顺序是从右到左,第二个括号是贪婪模式,第一个括号只能捕获一个字符,所以结果是`1`和`053`。 -其次,"后行断言"的反斜杠引用,也与通常的顺序相反,必须放在对应的那个括号之前。 +其次,“后行断言”的反斜杠引用,也与通常的顺序相反,必须放在对应的那个括号之前。 ```javascript /(?<=(o)d\1)r/.exec('hodor') // null @@ -388,7 +388,7 @@ const RE_DOLLAR_PREFIX = /(?<=\$)foo/g; ## Unicode 属性类 -目前,有一个[提案](https://github.com/mathiasbynens/es-regexp-unicode-property-escapes),引入了一种新的类的写法`\p{...}`和`\P{...}`,允许正则表达式匹配符合 Unicode 某种属性的所有字符。 +ES2018 [引入](https://github.com/tc39/proposal-regexp-unicode-property-escapes)了一种新的类的写法`\p{...}`和`\P{...}`,允许正则表达式匹配符合 Unicode 某种属性的所有字符。 ```javascript const regexGreekSymbol = /\p{Script=Greek}/u; @@ -403,10 +403,11 @@ Unicode 属性类要指定属性名和属性值。 \p{UnicodePropertyName=UnicodePropertyValue} ``` -对于某些属性,可以只写属性名。 +对于某些属性,可以只写属性名,或者只写属性值。 ```javascript \p{UnicodePropertyName} +\p{UnicodePropertyValue} ``` `\P{…}`是`\p{…}`的反向匹配,即匹配不满足条件的字符。 @@ -435,12 +436,18 @@ regex.test('ⅠⅡⅢⅣⅤⅥⅦⅧⅨⅩⅪⅫ') // true 下面是其他一些例子。 ```javascript +// 匹配所有空格 +\p{White_Space} + // 匹配各种文字的所有字母,等同于 Unicode 版的 \w [\p{Alphabetic}\p{Mark}\p{Decimal_Number}\p{Connector_Punctuation}\p{Join_Control}] // 匹配各种文字的所有非字母的字符,等同于 Unicode 版的 \W [^\p{Alphabetic}\p{Mark}\p{Decimal_Number}\p{Connector_Punctuation}\p{Join_Control}] +// 匹配 Emoji +/\p{Emoji_Modifier_Base}\p{Emoji_Modifier}?|\p{Emoji_Presentation}|\p{Emoji}\uFE0F/gu + // 匹配所有的箭头字符 const regexArrows = /^\p{Block=Arrows}+$/u; regexArrows.test('←↑→↓↔↕↖↗↘↙⇏⇐⇑⇒⇓⇔⇕⇖⇗⇘⇙⇧⇩') // true @@ -459,15 +466,17 @@ const RE_DATE = /(\d{4})-(\d{2})-(\d{2})/; 上面代码中,正则表达式里面有三组圆括号。使用`exec`方法,就可以将这三组匹配结果提取出来。 ```javascript +const RE_DATE = /(\d{4})-(\d{2})-(\d{2})/; + const matchObj = RE_DATE.exec('1999-12-31'); const year = matchObj[1]; // 1999 const month = matchObj[2]; // 12 const day = matchObj[3]; // 31 ``` -组匹配的一个问题是,每一组的匹配含义不容易看出来,而且只能用数字序号引用,要是组的顺序变了,引用的时候就必须修改序号。 +组匹配的一个问题是,每一组的匹配含义不容易看出来,而且只能用数字序号(比如`matchObj[1]`)引用,要是组的顺序变了,引用的时候就必须修改序号。 -现在有一个“具名组匹配”(Named Capture Groups)的[提案](https://github.com/tc39/proposal-regexp-named-groups),允许为每一个组匹配指定一个名字,既便于阅读代码,又便于引用。 +ES2018 引入了[具名组匹配](https://github.com/tc39/proposal-regexp-named-groups)(Named Capture Groups),允许为每一个组匹配指定一个名字,既便于阅读代码,又便于引用。 ```javascript const RE_DATE = /(?\d{4})-(?\d{2})-(?\d{2})/; @@ -559,3 +568,53 @@ const RE_TWICE = /^(?[a-z]+)!\k!\1$/; RE_TWICE.test('abc!abc!abc') // true RE_TWICE.test('abc!abc!ab') // false ``` + +## String.prototype.matchAll + +如果一个正则表达式在字符串里面有多个匹配,现在一般使用`g`修饰符或`y`修饰符,在循环里面逐一取出。 + +```javascript +var regex = /t(e)(st(\d?))/g; +var string = 'test1test2test3'; + +var matches = []; +var match; +while (match = regex.exec(string)) { + matches.push(match); +} + +matches +// [ +// ["test1", "e", "st1", "1", index: 0, input: "test1test2test3"], +// ["test2", "e", "st2", "2", index: 5, input: "test1test2test3"], +// ["test3", "e", "st3", "3", index: 10, input: "test1test2test3"] +// ] +``` + +上面代码中,`while`循环取出每一轮的正则匹配,一共三轮。 + +目前有一个[提案](https://github.com/tc39/proposal-string-matchall),增加了`String.prototype.matchAll`方法,可以一次性取出所有匹配。不过,它返回的是一个遍历器(Iterator),而不是数组。 + +```javascript +const string = 'test1test2test3'; +const regex = /t(e)(st(\d?))/g; +for (const match of string.matchAll(regex)) { + console.log(match); +} +// ["test1", "e", "st1", "1", index: 0, input: "test1test2test3"] +// ["test2", "e", "st2", "2", index: 5, input: "test1test2test3"] +// ["test3", "e", "st3", "3", index: 10, input: "test1test2test3"] +``` + +上面代码中,由于`string.matchAll(regex)`返回的是遍历器,所以可以用`for...of`循环取出。相对于返回数组,返回遍历器的好处在于,如果匹配结果是一个很大的数组,那么遍历器比较节省资源。 + +遍历器转为数组是非常简单的,使用`...`运算符和`Array.from`方法就可以了。 + +```javascript +// 转为数组方法一 +[...string.matchAll(regex)] + +// 转为数组方法二 +Array.from(string.matchAll(regex)); +``` + diff --git a/docs/string.md b/docs/string.md index 2101f4178..1e6528d45 100644 --- a/docs/string.md +++ b/docs/string.md @@ -349,6 +349,10 @@ ES2017 引入了字符串补全长度的功能。如果某个字符串不够指 '09-12'.padStart(10, 'YYYY-MM-DD') // "YYYY-09-12" ``` +## matchAll() + +`matchAll`方法返回一个正则表达式在当前字符串的所有匹配,详见《正则的扩展》的一章。 + ## 模板字符串 传统的 JavaScript 语言,输出模板通常是这样写的。 From b2a2c581bb059a98ad1fdac9287a8c65aeb9aa5c Mon Sep 17 00:00:00 2001 From: ruanyf Date: Fri, 26 Jan 2018 05:38:46 +0800 Subject: [PATCH 077/595] docs(function): edit error --- docs/function.md | 6 ++++-- docs/symbol.md | 22 ++++++++++++++-------- 2 files changed, 18 insertions(+), 10 deletions(-) diff --git a/docs/function.md b/docs/function.md index 5fa0d97c3..e80020675 100644 --- a/docs/function.md +++ b/docs/function.md @@ -1331,7 +1331,7 @@ clownsEverywhere( ## catch 语句的参数 -目前,有一个[提案](https://github.com/tc39/proposal-optional-catch-binding),允许`try...catch`结构中的`catch`语句调用时不带有参数。这个提案跟参数有关,也放在这一章介绍。 +ES2018 [允许](https://github.com/tc39/proposal-optional-catch-binding)`try...catch`结构的`catch`语句不带有参数。这个提案跟参数有关,也放在这一章介绍。 传统的写法是`catch`语句必须带有参数,用来接收`try`代码块抛出的错误。 @@ -1343,6 +1343,8 @@ try { } ``` +上面代码中,即使没有用到`error`,传统写法也是不允许省略。 + 新的写法允许省略`catch`后面的参数,而不报错。 ```javascript @@ -1364,4 +1366,4 @@ try { } ``` -上面代码中,`JSON.parse`报错只有一种可能:解析失败。因此,可以不需要抛出的错误实例。 +上面代码中,`JSON.parse`报错只有一种可能:解析失败。所以一旦报错,基本上不需要用到错误实例,所以可以省略`catch`后面的参数。 diff --git a/docs/symbol.md b/docs/symbol.md index 66db7062a..b357a65da 100644 --- a/docs/symbol.md +++ b/docs/symbol.md @@ -569,13 +569,17 @@ a2[1] = 6; class MyArray extends Array { } -const a = new MyArray(); -a.map(x => x) instanceof MyArray // true +const a = new MyArray(1, 2, 3); +const b = a.map(x => x); +const c = a.filter(x => x > 1); + +b instanceof MyArray // true +c instanceof MyArray // true ``` -上面代码中,子类`MyArray`继承了父类`Array`。`a.map(x => x)`会创建一个`MyArray`的衍生对象,该衍生对象还是`MyArray`的实例。 +上面代码中,子类`MyArray`继承了父类`Array`,`a`是`MyArray`的实例,`b`和`c`是`a`的衍生对象。你可能会认为,`b`和`c`都是调用数组方法生成的,所以应该是数组(`Array`的实例),但实际上它们也是`MyArray`的实例。 -现在,`MyArray`设置`Symbol.species`属性。 +`Symbol.species`属性就是为了解决这个问题而提供的。现在,我们可以为`MyArray`设置`Symbol.species`属性。 ```javascript class MyArray extends Array { @@ -583,7 +587,7 @@ class MyArray extends Array { } ``` -上面代码中,由于定义了`Symbol.species`属性,创建衍生对象时就会使用这个属性返回的函数,作为构造函数。这个例子也说明,定义`Symbol.species`属性要采用`get`读取器。默认的`Symbol.species`属性等同于下面的写法。 +上面代码中,由于定义了`Symbol.species`属性,创建衍生对象时就会使用这个属性返回的函数,作为构造函数。这个例子也说明,定义`Symbol.species`属性要采用`get`取值器。默认的`Symbol.species`属性等同于下面的写法。 ```javascript static get [Symbol.species]() { @@ -599,11 +603,13 @@ class MyArray extends Array { } const a = new MyArray(); -a.map(x => x) instanceof MyArray // false -a.map(x => x) instanceof Array // true +const b = a.map(x => x); + +b instanceof MyArray // false +b instanceof Array // true ``` -上面代码中,`a.map(x => x)`创建的衍生对象,就不是`MyArray`的实例,而直接就是`Array`的实例。 +上面代码中,`a.map(x => x)`生成的衍生对象,就不是`MyArray`的实例,而直接就是`Array`的实例。 再看一个例子。 From c669887292818f1e22baf1ab1900dfcd939f85b4 Mon Sep 17 00:00:00 2001 From: picc-lu <32639909+picc-lu@users.noreply.github.com> Date: Sat, 27 Jan 2018 16:48:47 +0800 Subject: [PATCH 078/595] Update Array MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 对Array.prototype.fill 方法作补充 --- docs/array.md | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/docs/array.md b/docs/array.md index 2246ec7ab..0352bba64 100644 --- a/docs/array.md +++ b/docs/array.md @@ -628,6 +628,20 @@ new Array(3).fill(7) 上面代码表示,`fill`方法从 1 号位开始,向原数组填充 7,到 2 号位之前结束。 +注意,如果填充的类型为对象,那么被赋值的是同一个内存地址的对象,而不是深拷贝对象。 + +```javascript +let arr = new Array(3).fill({name: "Mike"}); +arr[0].name = "Ben"; +arr +// [{name: "Ben"}, {name: "Ben"}, {name: "Ben"}] + +let arr = new Array(3).fill([]); +arr[0].push(5); +arr +// [[5], [5], [5]] +``` + ## 数组实例的 entries(),keys() 和 values() ES6 提供三个新的方法——`entries()`,`keys()`和`values()`——用于遍历数组。它们都返回一个遍历器对象(详见《Iterator》一章),可以用`for...of`循环进行遍历,唯一的区别是`keys()`是对键名的遍历、`values()`是对键值的遍历,`entries()`是对键值对的遍历。 From cef2b36a82edd9f4c16e7180d54e4dbb8094e037 Mon Sep 17 00:00:00 2001 From: ruanyf Date: Sun, 28 Jan 2018 09:24:08 +0800 Subject: [PATCH 079/595] =?UTF-8?q?docs(module):=20=E5=8A=A0=E5=85=A5=20im?= =?UTF-8?q?port=20=E5=91=BD=E4=BB=A4=E7=9A=84=E5=8F=AA=E8=AF=BB=E6=80=A7?= =?UTF-8?q?=20#595?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/module.md | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/docs/module.md b/docs/module.md index 78d5ca2c1..ba8664cbc 100644 --- a/docs/module.md +++ b/docs/module.md @@ -6,7 +6,7 @@ 在 ES6 之前,社区制定了一些模块加载方案,最主要的有 CommonJS 和 AMD 两种。前者用于服务器,后者用于浏览器。ES6 在语言标准的层面上,实现了模块功能,而且实现得相当简单,完全可以取代 CommonJS 和 AMD 规范,成为浏览器和服务器通用的模块解决方案。 -ES6 模块的设计思想,是尽量的静态化,使得编译时就能确定模块的依赖关系,以及输入和输出的变量。CommonJS 和 AMD 模块,都只能在运行时确定这些东西。比如,CommonJS 模块就是对象,输入时必须查找对象属性。 +ES6 模块的设计思想是尽量的静态化,使得编译时就能确定模块的依赖关系,以及输入和输出的变量。CommonJS 和 AMD 模块,都只能在运行时确定这些东西。比如,CommonJS 模块就是对象,输入时必须查找对象属性。 ```javascript // CommonJS模块 @@ -205,6 +205,24 @@ function setName(element) { import { lastName as surname } from './profile.js'; ``` +`import`命令输入的变量都是只读的,因为它的本质是输入接口。也就是说,不允许在加载模块的脚本里面,改写接口。 + +```javascript +import {a} from './xxx.js' + +a = {}; // Syntax Error : 'a' is read-only; +``` + +上面代码中,脚本加载了变量`a`,对其重新赋值就会报错,因为`a`是一个只读的接口。但是,如果`a`是一个对象,改写`a`的属性是允许的。 + +```javascript +import {a} from './xxx.js' + +a.foo = 'hello'; // 合法操作 +``` + +上面代码中,`a`的属性可以成功改写,并且其他模块也可以读到改写后的值。不过,这种写法很难查错,建议凡是输入的变量,都当作完全只读,轻易不要改变它的属性。 + `import`后面的`from`指定模块文件的位置,可以是相对路径,也可以是绝对路径,`.js`后缀可以省略。如果只是模块名,不带有路径,那么必须有配置文件,告诉 JavaScript 引擎该模块的位置。 ```javascript From eefe534d4fea1d3cc02cec5b6b6819558742a4d7 Mon Sep 17 00:00:00 2001 From: ruanyf Date: Sun, 28 Jan 2018 09:57:54 +0800 Subject: [PATCH 080/595] =?UTF-8?q?docs(symbol):=20=E4=BF=AE=E6=94=B9=20Si?= =?UTF-8?q?ngleton=20=E7=9A=84=E5=AE=9E=E4=BE=8B=20#597?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/symbol.md | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/docs/symbol.md b/docs/symbol.md index b357a65da..90126a310 100644 --- a/docs/symbol.md +++ b/docs/symbol.md @@ -422,11 +422,13 @@ console.log(a.foo); 但是,这里有一个问题,全局变量`global._foo`是可写的,任何文件都可以修改。 ```javascript +global._foo = { foo: 'world' }; + const a = require('./mod.js'); -global._foo = 123; +console.log(a.foo); ``` -上面的代码,会使得别的脚本加载`mod.js`都失真。 +上面的代码,会使得加载`mod.js`的脚本都失真。 为了防止这种情况出现,我们就可以使用 Symbol。 @@ -448,8 +450,9 @@ module.exports = global[FOO_KEY]; 上面代码中,可以保证`global[FOO_KEY]`不会被无意间覆盖,但还是可以被改写。 ```javascript +global[Symbol.for('foo')] = { foo: 'world' }; + const a = require('./mod.js'); -global[Symbol.for('foo')] = 123; ``` 如果键名使用`Symbol`方法生成,那么外部将无法引用这个值,当然也就无法改写。 @@ -461,7 +464,7 @@ const FOO_KEY = Symbol('foo'); // 后面代码相同 …… ``` -上面代码将导致其他脚本都无法引用`FOO_KEY`。但这样也有一个问题,就是如果多次执行这个脚本,每次得到的`FOO_KEY`都是不一样的。虽然 Node 会将脚本的执行结果缓存,一般情况下,不会多次执行同一个脚本,但是用户可以手动清除缓存,所以也不是完全可靠。 +上面代码将导致其他脚本都无法引用`FOO_KEY`。但这样也有一个问题,就是如果多次执行这个脚本,每次得到的`FOO_KEY`都是不一样的。虽然 Node 会将脚本的执行结果缓存,一般情况下,不会多次执行同一个脚本,但是用户可以手动清除缓存,所以也不是绝对可靠。 ## 内置的 Symbol 值 From c1b0b3f510ca00181873bc3444923876bfeb6b2b Mon Sep 17 00:00:00 2001 From: Zheeeng Date: Sun, 28 Jan 2018 11:14:33 +0800 Subject: [PATCH 081/595] Update module.md --- docs/module.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/module.md b/docs/module.md index ba8664cbc..f0c059ca0 100644 --- a/docs/module.md +++ b/docs/module.md @@ -501,12 +501,12 @@ let o = new MyClass(); ```javascript export { foo, bar } from 'my_module'; -// 等同于 +// 可以简单理解为 import { foo, bar } from 'my_module'; export { foo, bar }; ``` -上面代码中,`export`和`import`语句可以结合在一起,写成一行。 +上面代码中,`export`和`import`语句可以结合在一起,写成一行。但需要注意的是实际上foo、bar并没有被导入到当前模块,不能直接使用foo、bar模块。 模块的接口改名和整体输出,也可以采用这种写法。 From cb025812e5fafab635eeb775e3f5d7c96f0e3133 Mon Sep 17 00:00:00 2001 From: ruanyf Date: Sun, 28 Jan 2018 12:30:46 +0800 Subject: [PATCH 082/595] =?UTF-8?q?docs(proposals):=20=E5=A2=9E=E5=8A=A0?= =?UTF-8?q?=20=E6=9C=80=E6=96=B0=E6=8F=90=E6=A1=88=20=E4=B8=80=E7=AB=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/function.md | 38 --------------- docs/let.md | 24 ---------- docs/proposals.md | 119 ++++++++++++++++++++++++++++++++++++++++++++++ sidebar.md | 1 + 4 files changed, 120 insertions(+), 62 deletions(-) create mode 100644 docs/proposals.md diff --git a/docs/function.md b/docs/function.md index e80020675..beaa2ac7c 100644 --- a/docs/function.md +++ b/docs/function.md @@ -1329,41 +1329,3 @@ clownsEverywhere( 这样的规定也使得,函数参数与数组和对象的尾逗号规则,保持一致了。 -## catch 语句的参数 - -ES2018 [允许](https://github.com/tc39/proposal-optional-catch-binding)`try...catch`结构的`catch`语句不带有参数。这个提案跟参数有关,也放在这一章介绍。 - -传统的写法是`catch`语句必须带有参数,用来接收`try`代码块抛出的错误。 - -```javascript -try { - // ··· -} catch (error) { - // ··· -} -``` - -上面代码中,即使没有用到`error`,传统写法也是不允许省略。 - -新的写法允许省略`catch`后面的参数,而不报错。 - -```javascript -try { - // ··· -} catch { - // ··· -} -``` - -新写法只在不需要错误实例的情况下有用,因此不及传统写法的用途广。 - -```javascript -let jsonData; -try { - jsonData = JSON.parse(str); -} catch { - jsonData = DEFAULT_DATA; -} -``` - -上面代码中,`JSON.parse`报错只有一种可能:解析失败。所以一旦报错,基本上不需要用到错误实例,所以可以省略`catch`后面的参数。 diff --git a/docs/let.md b/docs/let.md index a18adaa1b..112022907 100644 --- a/docs/let.md +++ b/docs/let.md @@ -435,30 +435,6 @@ if (true) function f() {} ``` -### do 表达式 - -本质上,块级作用域是一个语句,将多个操作封装在一起,没有返回值。 - -```javascript -{ - let t = f(); - t = t * t + 1; -} -``` - -上面代码中,块级作用域将两个语句封装在一起。但是,在块级作用域以外,没有办法得到`t`的值,因为块级作用域不返回值,除非`t`是全局变量。 - -现在有一个[提案](http://wiki.ecmascript.org/doku.php?id=strawman:do_expressions),使得块级作用域可以变为表达式,也就是说可以返回值,办法就是在块级作用域之前加上`do`,使它变为`do`表达式,然后就会返回内部最后执行的表达式的值。 - -```javascript -let x = do { - let t = f(); - t * t + 1; -}; -``` - -上面代码中,变量`x`会得到整个块级作用域的返回值(`t * t + 1`)。 - ## const 命令 ### 基本用法 diff --git a/docs/proposals.md b/docs/proposals.md new file mode 100644 index 000000000..e4e479fd8 --- /dev/null +++ b/docs/proposals.md @@ -0,0 +1,119 @@ +# 最新提案 + +本章介绍一些尚未进入标准、但很有希望的最新提案。 + +## do 表达式 + +本质上,块级作用域是一个语句,将多个操作封装在一起,没有返回值。 + +```javascript +{ + let t = f(); + t = t * t + 1; +} +``` + +上面代码中,块级作用域将两个语句封装在一起。但是,在块级作用域以外,没有办法得到`t`的值,因为块级作用域不返回值,除非`t`是全局变量。 + +现在有一个[提案](https://github.com/tc39/proposal-do-expressions),使得块级作用域可以变为表达式,也就是说可以返回值,办法就是在块级作用域之前加上`do`,使它变为`do`表达式,然后就会返回内部最后执行的表达式的值。 + +```javascript +let x = do { + let t = f(); + t * t + 1; +}; +``` + +上面代码中,变量`x`会得到整个块级作用域的返回值(`t * t + 1`)。 + +`do`表达式的逻辑非常简单:封装的是什么,就会返回什么。 + +```javascript +// 等同于 <表达式> +do { <表达式>; } + +// 等同于 <语句> +do { <语句> } +``` + +`do`表达式的好处是可以封装多个语句,让程序更加模块化,就像乐高积木那样一块块拼装起来。 + +```javascript +let x = do { + if (foo()) { f() } + else if (bar()) { g() } + else { h() } +}; +``` + +上面代码的本质,就是根据函数`foo`的执行结果,调用不同的函数,将返回结果赋给变量`x`。使用`do`表达式,就将这个操作的意图表达得非常简洁清晰。而且,`do`块级作用域提供了单独的作用域,内部操作可以与全局作用域隔绝。 + +值得一提的是,`do`表达式在 JSX 语法中非常好用。 + +```javascript +return ( + +) +``` + +上面代码中,如果不用`do`表达式,就只能用三元判断运算符(`?:`)。那样的话,一旦判断逻辑复杂,代码就会变得很不易读。 + +## throw 表达式 + +JavaScript 语法规定`throw`是一个命令,用来抛出错误,不能用于表达式之中。 + +```javascript +// 报错 +console.log(throw new Error()); +``` + +上面代码中,`console.log`的参数必须是一个表达式,如果是一个`throw`语句就会报错。 + +现在有一个[提案](https://github.com/tc39/proposal-throw-expressions),允许`throw`用于表达式。 + +```javascript +// 参数的默认值 +function save(filename = throw new TypeError("Argument required")) { +} + +// 箭头函数的返回值 +lint(ast, { + with: () => throw new Error("avoid using 'with' statements.") +}); + +// 条件表达式 +function getEncoder(encoding) { + const encoder = encoding === "utf8" ? + new UTF8Encoder() : + encoding === "utf16le" ? + new UTF16Encoder(false) : + encoding === "utf16be" ? + new UTF16Encoder(true) : + throw new Error("Unsupported encoding"); +} + +// 逻辑表达式 +class Product { + get id() { + return this._id; + } + set id(value) { + this._id = value || throw new Error("Invalid value"); + } +} +``` + +上面代码中,`throw`都出现在表达式里面。 + +语法上,`throw`表达式里面的`throw`不再是一个命令,而是一个运算符。为了避免与`throw`命令混淆,规定`throw`出现在行首,一律解释为`throw`语句,而不是`throw`表达式。 diff --git a/sidebar.md b/sidebar.md index 55a86c186..da5e6997b 100644 --- a/sidebar.md +++ b/sidebar.md @@ -32,6 +32,7 @@ 1. [编程风格](#docs/style) 1. [读懂规格](#docs/spec) 1. [ArrayBuffer](#docs/arraybuffer) +1. [最新提案](#docs/proposals) 1. [参考链接](#docs/reference) ## 其他 From 5d5f588899f1d89f2c534db5b3b9551f386295f5 Mon Sep 17 00:00:00 2001 From: ruanyf Date: Sun, 28 Jan 2018 13:02:07 +0800 Subject: [PATCH 083/595] =?UTF-8?q?docs(module):=20edit=20export=E5=92=8Ci?= =?UTF-8?q?mport=20=E6=B7=B7=E5=90=88=E5=86=99=E6=B3=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/module.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/module.md b/docs/module.md index f0c059ca0..0a0f2a95e 100644 --- a/docs/module.md +++ b/docs/module.md @@ -506,7 +506,7 @@ import { foo, bar } from 'my_module'; export { foo, bar }; ``` -上面代码中,`export`和`import`语句可以结合在一起,写成一行。但需要注意的是实际上foo、bar并没有被导入到当前模块,不能直接使用foo、bar模块。 +上面代码中,`export`和`import`语句可以结合在一起,写成一行。但需要注意的是,写成一行以后,`foo`和`bar`实际上并没有被导入当前模块,只是相当于对外转发了这两个接口,导致当前模块不能直接使用`foo`和`bar`。 模块的接口改名和整体输出,也可以采用这种写法。 From 14c45fe2f549e0f44f7a53b201576f84064f8734 Mon Sep 17 00:00:00 2001 From: ruanyf Date: Mon, 29 Jan 2018 08:47:04 +0800 Subject: [PATCH 084/595] doc: update to ES2018 --- docs/async.md | 2 +- docs/object.md | 58 ++++---------------- docs/promise.md | 137 ++++++++++++++++++++++++++-------------------- docs/proposals.md | 43 +++++++++++++++ docs/string.md | 2 +- 5 files changed, 134 insertions(+), 108 deletions(-) diff --git a/docs/async.md b/docs/async.md index 1c6ab57ff..d922b4197 100644 --- a/docs/async.md +++ b/docs/async.md @@ -661,7 +661,7 @@ async function logInOrder(urls) { 这里隐含着一个规定,`next`方法必须是同步的,只要调用就必须立刻返回值。也就是说,一旦执行`next`方法,就必须同步地得到`value`和`done`这两个属性。如果遍历指针正好指向同步操作,当然没有问题,但对于异步操作,就不太合适了。目前的解决方法是,Generator 函数里面的异步操作,返回一个 Thunk 函数或者 Promise 对象,即`value`属性是一个 Thunk 函数或者 Promise 对象,等待以后返回真正的值,而`done`属性则还是同步产生的。 -目前,有一个[提案](https://github.com/tc39/proposal-async-iteration),为异步操作提供原生的遍历器接口,即`value`和`done`这两个属性都是异步产生,这称为”异步遍历器“(Async Iterator)。 +ES2018 [引入](https://github.com/tc39/proposal-async-iteration)了”异步遍历器“(Async Iterator),为异步操作提供原生的遍历器接口,即`value`和`done`这两个属性都是异步产生。 ### 异步遍历的接口 diff --git a/docs/object.md b/docs/object.md index baa1d44dc..c5249725a 100644 --- a/docs/object.md +++ b/docs/object.md @@ -1255,11 +1255,11 @@ a // 1 b // [2, 3] ``` -ES2017 将这个运算符[引入](https://github.com/sebmarkbage/ecmascript-rest-spread)了对象。 +ES2018 将这个运算符[引入](https://github.com/sebmarkbage/ecmascript-rest-spread)了对象。 -**(1)解构赋值** +### 解构赋值 -对象的解构赋值用于从一个对象取值,相当于将所有可遍历的、但尚未被读取的属性,分配到指定的对象上面。所有的键和它们的值,都会拷贝到新对象上面。 +对象的解构赋值用于从一个对象取值,相当于将目标对象自身的所有可遍历的(enumerable)、但尚未被读取的属性,分配到指定的对象上面。所有的键和它们的值,都会拷贝到新对象上面。 ```javascript let { x, y, ...z } = { x: 1, y: 2, a: 3, b: 4 }; @@ -1331,7 +1331,7 @@ function baseFunction({ a, b }) { // ... } function wrapperFunction({ x, y, ...restConfig }) { - // 使用x和y参数进行操作 + // 使用 x 和 y 参数进行操作 // 其余参数传给原始函数 return baseFunction(restConfig); } @@ -1339,9 +1339,9 @@ function wrapperFunction({ x, y, ...restConfig }) { 上面代码中,原始函数`baseFunction`接受`a`和`b`作为参数,函数`wrapperFunction`在`baseFunction`的基础上进行了扩展,能够接受多余的参数,并且保留原始函数的行为。 -**(2)扩展运算符** +### 扩展运算符 -扩展运算符(`...`)用于取出参数对象的所有可遍历属性,拷贝到当前对象之中。 +对象的扩展运算符(`...`)用于取出参数对象的所有可遍历属性,拷贝到当前对象之中。 ```javascript let z = { a: 3, b: 4 }; @@ -1419,6 +1419,8 @@ let newVersion = { ```javascript let aWithDefaults = { x: 1, y: 2, ...a }; // 等同于 + even if property keys don’t clash, because objects record insertion order: + let aWithDefaults = Object.assign({}, { x: 1, y: 2 }, a); // 等同于 let aWithDefaults = Object.assign({ x: 1, y: 2 }, a); @@ -1438,6 +1440,8 @@ const obj = { ```javascript {...{}, a: 1} // { a: 1 } + even if property keys don’t clash, because objects record insertion order: + ``` 如果扩展运算符的参数是`null`或`undefined`,这两个值会被忽略,不会报错。 @@ -1468,45 +1472,3 @@ let runtimeError = { }; ``` -## Null 传导运算符 - -编程实务中,如果读取对象内部的某个属性,往往需要判断一下该对象是否存在。比如,要读取`message.body.user.firstName`,安全的写法是写成下面这样。 - -```javascript -const firstName = (message - && message.body - && message.body.user - && message.body.user.firstName) || 'default'; -``` - -这样的层层判断非常麻烦,因此现在有一个[提案](https://github.com/claudepache/es-optional-chaining),引入了“Null 传导运算符”(null propagation operator)`?.`,简化上面的写法。 - -```javascript -const firstName = message?.body?.user?.firstName || 'default'; -``` - -上面代码有三个`?.`运算符,只要其中一个返回`null`或`undefined`,就不再往下运算,而是返回`undefined`。 - -“Null 传导运算符”有四种用法。 - -- `obj?.prop` // 读取对象属性 -- `obj?.[expr]` // 同上 -- `func?.(...args)` // 函数或对象方法的调用 -- `new C?.(...args)` // 构造函数的调用 - -传导运算符之所以写成`obj?.prop`,而不是`obj?prop`,是为了方便编译器能够区分三元运算符`?:`(比如`obj?prop:123`)。 - -下面是更多的例子。 - -```javascript -// 如果 a 是 null 或 undefined, 返回 undefined -// 否则返回 a.b.c().d -a?.b.c().d - -// 如果 a 是 null 或 undefined,下面的语句不产生任何效果 -// 否则执行 a.b = 42 -a?.b = 42 - -// 如果 a 是 null 或 undefined,下面的语句不产生任何效果 -delete a?.b -``` diff --git a/docs/promise.md b/docs/promise.md index c87f0552b..4793c58b3 100644 --- a/docs/promise.md +++ b/docs/promise.md @@ -486,6 +486,85 @@ someAsyncThing().then(function() { 上面代码中,第二个`catch`方法用来捕获,前一个`catch`方法抛出的错误。 +## Promise.prototype.finally() + +`finally`方法用于指定不管 Promise 对象最后状态如何,都会执行的操作。该方法是 ES2018 引入标准的。 + +```javascript +promise +.then(result => {···}) +.catch(error => {···}) +.finally(() => {···}); +``` + +上面代码中,不管`promise`最后的状态,在执行完`then`或`catch`指定的回调函数以后,都会执行`finally`方法指定的回调函数。 + +下面是一个例子,服务器使用 Promise 处理请求,然后使用`finally`方法关掉服务器。 + +```javascript +server.listen(port) + .then(function () { + // ... + }) + .finally(server.stop); +``` + +`finally`方法的回调函数不接受任何参数,这意味着没有办法知道,前面的 Promise 状态到底是`fulfilled`还是`rejected`。这表明,`finally`方法里面的操作,应该是与状态无关的,不依赖于 Promise 的执行结果。 + +`finally`本质上是`then`方法的特例。 + +```javascript +promise +.finally(() => { + // 语句 +}); + +// 等同于 +promise +.then( + result => { + // 语句 + return result; + }, + error => { + // 语句 + throw error; + } +); +``` + +上面代码中,如果不使用`finally`方法,同样的语句需要为成功和失败两种情况各写一次。有了`finally`方法,则只需要写一次。 + +它的实现也很简单。 + +```javascript +Promise.prototype.finally = function (callback) { + let P = this.constructor; + return this.then( + value => P.resolve(callback()).then(() => value), + reason => P.resolve(callback()).then(() => { throw reason }) + ); +}; +``` + +上面代码中,不管前面的 Promise 是`fulfilled`还是`rejected`,都会执行回调函数`callback`。 + +从上面的实现还可以看到,`finally`方法总是会返回原来的值。 + +```javascript +// resolve 的值是 undefined +Promise.resolve(2).then(() => {}, () => {}) + +// resolve 的值是 2 +Promise.resolve(2).finally(() => {}) + +// reject 的值是 undefined +Promise.reject(3).then(() => {}, () => {}) + +// reject 的值是 3 +Promise.reject(3).finally(() => {}) +``` + ## Promise.all() `Promise.all`方法用于将多个 Promise 实例,包装成一个新的 Promise 实例。 @@ -747,64 +826,6 @@ Promise.reject(thenable) 上面代码中,`Promise.reject`方法的参数是一个`thenable`对象,执行以后,后面`catch`方法的参数不是`reject`抛出的“出错了”这个字符串,而是`thenable`对象。 -## 两个有用的附加方法 - -ES6 的 Promise API 提供的方法不是很多,有些有用的方法可以自己部署。下面介绍如何部署两个不在 ES6 之中、但很有用的方法。 - -### done() - -Promise 对象的回调链,不管以`then`方法或`catch`方法结尾,要是最后一个方法抛出错误,都有可能无法捕捉到(因为 Promise 内部的错误不会冒泡到全局)。因此,我们可以提供一个`done`方法,总是处于回调链的尾端,保证抛出任何可能出现的错误。 - -```javascript -asyncFunc() - .then(f1) - .catch(r1) - .then(f2) - .done(); -``` - -它的实现代码相当简单。 - -```javascript -Promise.prototype.done = function (onFulfilled, onRejected) { - this.then(onFulfilled, onRejected) - .catch(function (reason) { - // 抛出一个全局错误 - setTimeout(() => { throw reason }, 0); - }); -}; -``` - -从上面代码可见,`done`方法的使用,可以像`then`方法那样用,提供`fulfilled`和`rejected`状态的回调函数,也可以不提供任何参数。但不管怎样,`done`都会捕捉到任何可能出现的错误,并向全局抛出。 - -### finally() - -`finally`方法用于指定不管 Promise 对象最后状态如何,都会执行的操作。它与`done`方法的最大区别,它接受一个普通的回调函数作为参数,该函数不管怎样都必须执行。 - -下面是一个例子,服务器使用 Promise 处理请求,然后使用`finally`方法关掉服务器。 - -```javascript -server.listen(0) - .then(function () { - // run test - }) - .finally(server.stop); -``` - -它的实现也很简单。 - -```javascript -Promise.prototype.finally = function (callback) { - let P = this.constructor; - return this.then( - value => P.resolve(callback()).then(() => value), - reason => P.resolve(callback()).then(() => { throw reason }) - ); -}; -``` - -上面代码中,不管前面的 Promise 是`fulfilled`还是`rejected`,都会执行回调函数`callback`。 - ## 应用 ### 加载图片 diff --git a/docs/proposals.md b/docs/proposals.md index e4e479fd8..bc68caac0 100644 --- a/docs/proposals.md +++ b/docs/proposals.md @@ -117,3 +117,46 @@ class Product { 上面代码中,`throw`都出现在表达式里面。 语法上,`throw`表达式里面的`throw`不再是一个命令,而是一个运算符。为了避免与`throw`命令混淆,规定`throw`出现在行首,一律解释为`throw`语句,而不是`throw`表达式。 + +## Null 传导运算符 + +编程实务中,如果读取对象内部的某个属性,往往需要判断一下该对象是否存在。比如,要读取`message.body.user.firstName`,安全的写法是写成下面这样。 + +```javascript +const firstName = (message + && message.body + && message.body.user + && message.body.user.firstName) || 'default'; +``` + +这样的层层判断非常麻烦,因此现在有一个[提案](https://github.com/claudepache/es-optional-chaining),引入了“Null 传导运算符”(null propagation operator)`?.`,简化上面的写法。 + +```javascript +const firstName = message?.body?.user?.firstName || 'default'; +``` + +上面代码有三个`?.`运算符,只要其中一个返回`null`或`undefined`,就不再往下运算,而是返回`undefined`。 + +“Null 传导运算符”有四种用法。 + +- `obj?.prop` // 读取对象属性 +- `obj?.[expr]` // 同上 +- `func?.(...args)` // 函数或对象方法的调用 +- `new C?.(...args)` // 构造函数的调用 + +传导运算符之所以写成`obj?.prop`,而不是`obj?prop`,是为了方便编译器能够区分三元运算符`?:`(比如`obj?prop:123`)。 + +下面是更多的例子。 + +```javascript +// 如果 a 是 null 或 undefined, 返回 undefined +// 否则返回 a.b.c().d +a?.b.c().d + +// 如果 a 是 null 或 undefined,下面的语句不产生任何效果 +// 否则执行 a.b = 42 +a?.b = 42 + +// 如果 a 是 null 或 undefined,下面的语句不产生任何效果 +delete a?.b +``` diff --git a/docs/string.md b/docs/string.md index 1e6528d45..770ed08bc 100644 --- a/docs/string.md +++ b/docs/string.md @@ -933,7 +933,7 @@ Breve over the h goes \u{h}ere // 报错 模板字符串会将`\u00FF`和`\u{42}`当作 Unicode 字符进行转义,所以`\unicode`解析时报错;而`\x56`会被当作十六进制字符串转义,所以`\xerxes`会报错。也就是说,`\u`和`\x`在 LaTEX 里面有特殊含义,但是 JavaScript 将它们转义了。 -为了解决这个问题,现在有一个[提案](https://tc39.github.io/proposal-template-literal-revision/),放松对标签模板里面的字符串转义的限制。如果遇到不合法的字符串转义,就返回`undefined`,而不是报错,并且从`raw`属性上面可以得到原始字符串。 +为了解决这个问题,ES2018 [放松](https://tc39.github.io/proposal-template-literal-revision/)了对标签模板里面的字符串转义的限制。如果遇到不合法的字符串转义,就返回`undefined`,而不是报错,并且从`raw`属性上面可以得到原始字符串。 ```javascript function tag(strs) { From 461d3edae8e109ebd2a04d0a3e0e1d9425ffdcd5 Mon Sep 17 00:00:00 2001 From: ruanyf Date: Tue, 30 Jan 2018 14:59:18 +0800 Subject: [PATCH 085/595] docs(generator): edit generator --- docs/generator.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/generator.md b/docs/generator.md index ae00285fb..56f14e643 100644 --- a/docs/generator.md +++ b/docs/generator.md @@ -1076,6 +1076,7 @@ function* g() { } let obj = g(); +obj.next(); obj.a // undefined ``` From 537c06d8ded341e1087db3b0691b3026184f9bd6 Mon Sep 17 00:00:00 2001 From: LucasLee92 Date: Thu, 1 Feb 2018 14:20:25 +0800 Subject: [PATCH 086/595] =?UTF-8?q?set-map=20=E6=96=87=E7=AB=A0=E8=A1=A8?= =?UTF-8?q?=E8=BE=BE=E9=94=99=E8=AF=AF=E4=BF=AE=E6=AD=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/set-map.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/set-map.md b/docs/set-map.md index 2f7040f01..b25f144dc 100644 --- a/docs/set-map.md +++ b/docs/set-map.md @@ -877,7 +877,7 @@ jsonToStrMap('{"yes": true, "no": false}') // Map {'yes' => true, 'no' => false} ``` -但是,有一种特殊情况,整个 JSON 就是一个数组,且每个数组成员本身,又是一个有两个成员的数组。这时,它可以一一对应地转为 Map。这往往是数组转为 JSON 的逆操作。 +但是,有一种特殊情况,整个 JSON 就是一个数组,且每个数组成员本身,又是一个有两个成员的数组。这时,它可以一一对应地转为 Map。这往往是 Map 转为 数组 JSON 的逆操作。 ```javascript function jsonToMap(jsonStr) { From fb5ef8dc33ccce12e489f59311db4d4ebd5a57a6 Mon Sep 17 00:00:00 2001 From: ruanyf Date: Thu, 1 Feb 2018 18:47:58 +0800 Subject: [PATCH 087/595] docs(proposal): edit proposal --- docs/proposals.md | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/docs/proposals.md b/docs/proposals.md index bc68caac0..929adbb6e 100644 --- a/docs/proposals.md +++ b/docs/proposals.md @@ -160,3 +160,33 @@ a?.b = 42 // 如果 a 是 null 或 undefined,下面的语句不产生任何效果 delete a?.b ``` + +## 直接输入 U+2028 和 U+2029 + +JavaScript 字符串允许直接输入字符,以及输入字符的转义形式。举例来说,“中”的 Unicode 码点是 U+4e2d,你可以直接在字符串里面输入这个汉字,也可以输入它的转义形式`\u4e2d`,两者是等价的。 + +```javascript +'中' === '\u4e2d' // true +``` + +但是,JavaScript 规定有5个字符,不能在字符串里面直接使用,只能使用转义形式。 + +- U+005C:反斜杠(reverse solidus) +- U+000D:回车(carriage return) +- U+2028:行分隔符(line separator) +- U+2029:段分隔符(paragraph separator) +- U+000A:换行符(line feed) + +举例来说,字符串里面不能直接包含反斜杠,一定要转义写成`\\`或者`\u005c`。 + +这个规定本身没有问题,麻烦在于 JSON 格式允许字符串里面直接使用 U+2028(行分隔符)和 U+2029(段分隔符)。这样一来,服务器输出的 JSON 被`JSON.parse`解析,就有可能直接报错。 + +JSON 格式已经冻结(RFC 7159),没法修改了。为了消除这个报错,现在有一个[提案](https://github.com/tc39/proposal-json-superset),允许 JavaScript 字符串直接输入 U+2028(行分隔符)和 U+2029(段分隔符)。 + +```javascript +const PS = eval("'\u2029'"); +``` + +根据这个提案,上面的代码不会报错。 + +注意,模板字符串现在就允许直接输入这两个字符。另外,正则表达式依然不允许直接输入这两个字符,这是没有问题的,因为 JSON 本来就不允许直接包含正则表达式。 From 3bddf9cd881c6a2239d59e66733ecc8175125ff3 Mon Sep 17 00:00:00 2001 From: ruanyf Date: Fri, 2 Feb 2018 18:45:46 +0800 Subject: [PATCH 088/595] docs(set-map): edit set-map --- docs/set-map.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/set-map.md b/docs/set-map.md index b25f144dc..c3087ea3b 100644 --- a/docs/set-map.md +++ b/docs/set-map.md @@ -877,7 +877,7 @@ jsonToStrMap('{"yes": true, "no": false}') // Map {'yes' => true, 'no' => false} ``` -但是,有一种特殊情况,整个 JSON 就是一个数组,且每个数组成员本身,又是一个有两个成员的数组。这时,它可以一一对应地转为 Map。这往往是 Map 转为 数组 JSON 的逆操作。 +但是,有一种特殊情况,整个 JSON 就是一个数组,且每个数组成员本身,又是一个有两个成员的数组。这时,它可以一一对应地转为 Map。这往往是 Map 转为数组 JSON 的逆操作。 ```javascript function jsonToMap(jsonStr) { From 9be34c0bd8b40e900639e71af9792feeebf54830 Mon Sep 17 00:00:00 2001 From: ruanyf Date: Mon, 5 Feb 2018 05:39:34 +0800 Subject: [PATCH 089/595] docs(proposals): edit proposals --- docs/proposals.md | 186 ++++++++++++++++++++++++++++++++++++++++++++++ docs/reflect.md | 18 +++++ 2 files changed, 204 insertions(+) diff --git a/docs/proposals.md b/docs/proposals.md index 929adbb6e..91f921eed 100644 --- a/docs/proposals.md +++ b/docs/proposals.md @@ -190,3 +190,189 @@ const PS = eval("'\u2029'"); 根据这个提案,上面的代码不会报错。 注意,模板字符串现在就允许直接输入这两个字符。另外,正则表达式依然不允许直接输入这两个字符,这是没有问题的,因为 JSON 本来就不允许直接包含正则表达式。 + +## 函数的部分执行 + +### 语法 + +多参数的函数有时需要绑定其中的一个或多个函数,然后返回一个新函数。 + +```javascript +function add(x, y) { return x + y; } +function add7(x) { return x + 7; } +``` + +上面代码中,`add7`函数其实是`add`函数的一个特殊版本,通过将一个参数绑定为`7`,就可以从`add`得到`add7`。 + +```javascript +// bind 方法 +const add7 = add.bind(null, 7); + +// 箭头函数 +const add7 = x => add(x, 7); +``` + +上面两种写法都有些冗余。其中,`bind`方法的局限更加明显,它必须提供`this`,并且只能从前到后一个个绑定参数,无法只绑定非头部的参数。 + +现在有一个[提案](https://github.com/tc39/proposal-partial-application),使用绑定参数然后返回一个新函数更加容器。这叫做函数的部分执行(partial application)。 + +```javascript +const add = (x, y) => x + y; +const addOne = add(1, ?); + +const maxGreaterThanZero = Math.max(0, ...); +``` + +根据新提案,`?`是单个参数的占位符,`...`是多个参数的占位符。以下的形式都属于函数的部分执行。 + +```javascript +f(x, ?) +f(x, ...) +f(?, x) +f(..., x) +f(?, x, ?) +f(..., x, ...) +``` + +`?`和`...`只能出现在函数的调用之中,并且会返回一个新函数。 + +```javascript +const g = f(?, 1, ...); +// 等同于 +const g = (x, ...y) => f(x, 1, ...y); +``` + +### 注意点 + +函数的部分执行有一些特别注意的地方。 + +(1)函数的部分执行是基于原函数的。如果原函数发生变化,部分执行生成的新函数也会立即反映这种变化。 + +```javascript +let f = (x, y) => x + y; + +const g = f(?, 3); +g(1); // 4 + +// 替换函数 f +f = (x, y) => x * y; + +g(1); // 3 +``` + +上面代码中,定义了函数的部分执行以后,更换原函数会立即影响到新函数。 + +(2)如果预先提供的那个值是一个表达式,那么这个表达式并不会在定义时求值,而是在每次调用时求值。 + +```javascript +let a = 3; +const f = (x, y) => x + y; + +const g = f(?, a); +g(1); // 4 + +// 改变 a 的值 +a = 10; +g(1); // 11 +``` + +上面代码中,预先提供的参数是变量`a`,那么每次调用函数`g`的时候,才会对`a`进行求值。 + +(3)如果新函数的参数多于占位符的数量,那么多余的参数将被忽略。 + +```javascript +const f = (x, ...y) => [x, ...y]; +const g = f(?, 1); +g(2, 3, 4); // [2, 1] +``` + +上面代码中,函数`g`只有一个占位符,也就意味着它只能接受一个参数,多余的参数都会被忽略。 + +写成下面这样,多余的参数就没有问题。 + +```javascript +const f = (x, ...y) => [x, ...y]; +const g = f(?, 1, ...); +g(2, 3, 4); // [2, 1, 3, 4]; +``` + +(4)`...`只会被采集一次,如果函数的部分执行使用了多个`...`,那么每个`...`的值都将相同。 + +```javascript +const f = (...x) => x; +const g = f(..., 9, ...); +g(1, 2, 3); // [1, 2, 3, 9, 1, 2, 3] +``` + +上面代码中,`g`定义了两个`...`占位符,真正执行的时候,它们的值是一样的。 + +## 管道运算符 + +Unix 操作系统有一个管道机制(pipeline),可以把前一个操作的值传给后一个操作。这个机制非常有用,使得简单的操作可以组合成为复杂的操作。许多语言都有管道的实现,现在有一个[提案](https://github.com/tc39/proposal-partial-application),让 JavaScript 也拥有管道机制。 + +JavaScript 的管道是一个运算符,写作`|>`。它的左边是一个表达式,右边是一个函数。管道运算符把左边表达式的值,传入右边的函数进行求职。 + +```javascript +x |> f +// 等同于 +f(x) +``` + +管道运算符最大的好处,就是可以把嵌套的函数,写成从左到右的链式表达式。 + +```javascript +function doubleSay (str) { + return str + ", " + str; +} + +function capitalize (str) { + return str[0].toUpperCase() + str.substring(1); +} + +function exclaim (str) { + return str + '!'; +} +``` + +上面是三个简单的函数。如果要嵌套执行,传统的写法和管道的写法分别如下。 + +```javascript +// 传统的写法 +exclaim(capitalize(doubleSay('hello'))) +// "Hello, hello!" + +// 管道的写法 +'hello' + |> doubleSay + |> capitalize + |> exclaim +// "Hello, hello!" +``` + +管道运算符只能传递一个值,这意味着它右边的函数必须是一个单参数函数。如果是多参数函数,就必须进行柯里化,改成单参数的版本。 + +```javascript +function double (x) { return x + x; } +function add (x, y) { return x + y; } + +let person = { score: 25 }; +person.score + |> double + |> (_ => add(7, _)) +// 57 +``` + +上面代码中,`add`函数需要两个参数。但是,管道运算符只能传入一个值,因此需要事先提供另一个参数,并将其改成单参数的箭头函数`_ => add(7, _)`。这个函数里面的下划线并没有特别的含义,可以用其他符号代替,使用下划线只是因为,它能够形象地表示这里是占位符。 + +管道运算符对于`await`函数也适用。 + +```javascript +x |> await f +// 等同于 +await f(x) + +const userAge = userId |> await fetchUserById |> getAgeFromUser; +// 等同于 +const userAge = getAgeFromUser(await fetchUserById(userId)); +``` + diff --git a/docs/reflect.md b/docs/reflect.md index 6cd9c38af..ff3fd579b 100644 --- a/docs/reflect.md +++ b/docs/reflect.md @@ -387,6 +387,24 @@ Reflect.defineProperty(MyDate, 'now', { 如果`Reflect.defineProperty`的第一个参数不是对象,就会抛出错误,比如`Reflect.defineProperty(1, 'foo')`。 +这个方法可以与`Proxy.defineProperty`配合使用。 + +```javascript +const p = new Proxy({}, { + defineProperty(target, prop, descriptor) { + console.log(descriptor); + return Reflect.defineProperty(target, prop, descriptor); + } +}); + +p.foo = 'bar'; +// {value: "bar", writable: true, enumerable: true, configurable: true} + +p.foo // "bar" +``` + +上面代码中,`Proxy.defineProperty`对属性赋值设置了拦截,然后使用`Reflect.defineProperty`完成了赋值。 + ### Reflect.getOwnPropertyDescriptor(target, propertyKey) `Reflect.getOwnPropertyDescriptor`基本等同于`Object.getOwnPropertyDescriptor`,用于得到指定属性的描述对象,将来会替代掉后者。 From d6045d09c96ed0237af2fe663e546d59b35e923e Mon Sep 17 00:00:00 2001 From: ruanyf Date: Mon, 5 Feb 2018 10:22:21 +0800 Subject: [PATCH 090/595] docs(proposals): edit proposals --- docs/proposals.md | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/docs/proposals.md b/docs/proposals.md index 91f921eed..3d65d887d 100644 --- a/docs/proposals.md +++ b/docs/proposals.md @@ -242,6 +242,17 @@ const g = f(?, 1, ...); const g = (x, ...y) => f(x, 1, ...y); ``` +函数的部分执行,也可以用于对象的方法。 + +```javascript +let obj = { + f(x, y) { return x + y; }, +}; + +const g = obj.f(?, 3); +g(1) // 4 +``` + ### 注意点 函数的部分执行有一些特别注意的地方。 From 3c4b9d1a63bbd5559b875d5801f88b4751d0ca78 Mon Sep 17 00:00:00 2001 From: ruanyf Date: Thu, 8 Feb 2018 18:17:29 +0800 Subject: [PATCH 091/595] docs(arraybuffer): edit arraybuffer --- docs/arraybuffer.md | 12 +++++++++++- docs/proposals.md | 2 +- docs/set-map.md | 2 +- 3 files changed, 13 insertions(+), 3 deletions(-) diff --git a/docs/arraybuffer.md b/docs/arraybuffer.md index bc0cfda40..098031c85 100644 --- a/docs/arraybuffer.md +++ b/docs/arraybuffer.md @@ -454,7 +454,17 @@ Float64Array.BYTES_PER_ELEMENT // 8 function ab2str(buf) { // 注意,如果是大型二进制数组,为了避免溢出, // 必须一个一个字符地转 - return String.fromCharCode.apply(null, new Uint16Array(buf)); + if (buf && buf.byteLength < 1024) { + return String.fromCharCode.apply(null, new Uint16Array(buf)); + } + + const bufView = new Uint16Array(buf); + const len = bufView.length; + const bstr = new Array(len); + for (let i = 0; i < len; i++) { + bstr[i] = String.fromCharCode.call(null, bufView[i]); + } + return bstr.join(''); } // 字符串转为 ArrayBuffer 对象,参数为字符串 diff --git a/docs/proposals.md b/docs/proposals.md index 3d65d887d..886106957 100644 --- a/docs/proposals.md +++ b/docs/proposals.md @@ -321,7 +321,7 @@ g(1, 2, 3); // [1, 2, 3, 9, 1, 2, 3] Unix 操作系统有一个管道机制(pipeline),可以把前一个操作的值传给后一个操作。这个机制非常有用,使得简单的操作可以组合成为复杂的操作。许多语言都有管道的实现,现在有一个[提案](https://github.com/tc39/proposal-partial-application),让 JavaScript 也拥有管道机制。 -JavaScript 的管道是一个运算符,写作`|>`。它的左边是一个表达式,右边是一个函数。管道运算符把左边表达式的值,传入右边的函数进行求职。 +JavaScript 的管道是一个运算符,写作`|>`。它的左边是一个表达式,右边是一个函数。管道运算符把左边表达式的值,传入右边的函数进行求值。 ```javascript x |> f diff --git a/docs/set-map.md b/docs/set-map.md index c3087ea3b..27f109e13 100644 --- a/docs/set-map.md +++ b/docs/set-map.md @@ -55,7 +55,7 @@ set.size // 56 [...new Set(array)] ``` -向 Set 加入值的时候,不会发生类型转换,所以`5`和`"5"`是两个不同的值。Set 内部判断两个值是否不同,使用的算法叫做“Same-value equality”,它类似于精确相等运算符(`===`),主要的区别是`NaN`等于自身,而精确相等运算符认为`NaN`不等于自身。 +向 Set 加入值的时候,不会发生类型转换,所以`5`和`"5"`是两个不同的值。Set 内部判断两个值是否不同,使用的算法叫做“Same-value-zero equality”,它类似于精确相等运算符(`===`),主要的区别是`NaN`等于自身,而精确相等运算符认为`NaN`不等于自身。 ```javascript let set = new Set(); From b70cef123ffff72432e07d114d35deadd2570da5 Mon Sep 17 00:00:00 2001 From: ruanyf Date: Thu, 8 Feb 2018 18:51:42 +0800 Subject: [PATCH 092/595] docs(proposals): edit proposals --- docs/number.md | 117 ---------------------------- docs/proposals.md | 194 ++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 194 insertions(+), 117 deletions(-) diff --git a/docs/number.md b/docs/number.md index 3e477f6ed..478db3efd 100644 --- a/docs/number.md +++ b/docs/number.md @@ -655,38 +655,6 @@ ES6 新增了 6 个双曲函数方法。 - `Math.acosh(x)` 返回`x`的反双曲余弦(inverse hyperbolic cosine) - `Math.atanh(x)` 返回`x`的反双曲正切(inverse hyperbolic tangent) -## Math.signbit() - -`Math.sign()`用来判断一个值的正负,但是如果参数是`-0`,它会返回`-0`。 - -```javascript -Math.sign(-0) // -0 -``` - -这导致对于判断符号位的正负,`Math.sign()`不是很有用。JavaScript 内部使用 64 位浮点数(国际标准 IEEE 754)表示数值,IEEE 754 规定第一位是符号位,`0`表示正数,`1`表示负数。所以会有两种零,`+0`是符号位为`0`时的零值,`-0`是符号位为`1`时的零值。实际编程中,判断一个值是`+0`还是`-0`非常麻烦,因为它们是相等的。 - -```javascript -+0 === -0 // true -``` - -目前,有一个[提案](http://jfbastien.github.io/papers/Math.signbit.html),引入了`Math.signbit()`方法判断一个数的符号位是否设置了。 - -```javascript -Math.signbit(2) //false -Math.signbit(-2) //true -Math.signbit(0) //false -Math.signbit(-0) //true -``` - -可以看到,该方法正确返回了`-0`的符号位是设置了的。 - -该方法的算法如下。 - -- 如果参数是`NaN`,返回`false` -- 如果参数是`-0`,返回`true` -- 如果参数是负值,返回`true` -- 其他情况返回`false` - ## 指数运算符 ES2016 新增了一个指数运算符(`**`)。 @@ -719,88 +687,3 @@ Math.pow(99, 99) ``` 上面代码中,两个运算结果的最后一位有效数字是有差异的。 - -## Integer 数据类型 - -### 简介 - -JavaScript 所有数字都保存成 64 位浮点数,这决定了整数的精确程度只能到 53 个二进制位。大于这个范围的整数,JavaScript 是无法精确表示的,这使得 JavaScript 不适合进行科学和金融方面的精确计算。 - -现在有一个[提案](https://github.com/tc39/proposal-bigint),引入了新的数据类型 Integer(整数),来解决这个问题。整数类型的数据只用来表示整数,没有位数的限制,任何位数的整数都可以精确表示。 - -为了与 Number 类型区别,Integer 类型的数据必须使用后缀`n`表示。 - -```javascript -1n + 2n // 3n -``` - -二进制、八进制、十六进制的表示法,都要加上后缀`n`。 - -```javascript -0b1101n // 二进制 -0o777n // 八进制 -0xFFn // 十六进制 -``` - -`typeof`运算符对于 Integer 类型的数据返回`integer`。 - -```javascript -typeof 123n -// 'integer' -``` - -JavaScript 原生提供`Integer`对象,用来生成 Integer 类型的数值。转换规则基本与`Number()`一致。 - -```javascript -Integer(123) // 123n -Integer('123') // 123n -Integer(false) // 0n -Integer(true) // 1n -``` - -以下的用法会报错。 - -```javascript -new Integer() // TypeError -Integer(undefined) //TypeError -Integer(null) // TypeError -Integer('123n') // SyntaxError -Integer('abc') // SyntaxError -``` - -### 运算 - -在数学运算方面,Integer 类型的`+`、`-`、`*`和`**`这四个二元运算符,与 Number 类型的行为一致。除法运算`/`会舍去小数部分,返回一个整数。 - -```javascript -9n / 5n -// 1n -``` - -几乎所有的 Number 运算符都可以用在 Integer,但是有两个除外:不带符号的右移位运算符`>>>`和一元的求正运算符`+`,使用时会报错。前者是因为`>>>`要求最高位补 0,但是 Integer 类型没有最高位,导致这个运算符无意义。后者是因为一元运算符`+`在 asm.js 里面总是返回 Number 类型或者报错。 - -Integer 类型不能与 Number 类型进行混合运算。 - -```javascript -1n + 1 -// 报错 -``` - -这是因为无论返回的是 Integer 或 Number,都会导致丢失信息。比如`(2n**53n + 1n) + 0.5`这个表达式,如果返回 Integer 类型,`0.5`这个小数部分会丢失;如果返回 Number 类型,会超过 53 位精确数字,精度下降。 - -相等运算符(`==`)会改变数据类型,也是不允许混合使用。 - -```javascript -0n == 0 -// 报错 TypeError - -0n == false -// 报错 TypeError -``` - -精确相等运算符(`===`)不会改变数据类型,因此可以混合使用。 - -```javascript -0n === 0 -// false -``` diff --git a/docs/proposals.md b/docs/proposals.md index 886106957..6d0b2dedc 100644 --- a/docs/proposals.md +++ b/docs/proposals.md @@ -387,3 +387,197 @@ const userAge = userId |> await fetchUserById |> getAgeFromUser; const userAge = getAgeFromUser(await fetchUserById(userId)); ``` +## 数值分隔符 + +欧美语言中,较长的数值允许每三位添加一个分隔符(通常是一个逗号),增加数值的可读性。比如,`1000`可以写作`1,000`。 + +现在有一个[提案](https://github.com/tc39/proposal-numeric-separator),允许 JavaScript 的数值使用下划线(`_`)作为分隔符。 + +```javascript +let budget = 1_000_000_000_000; +budget === 10 ** 12 // true +``` + +JavaScript 的数值分隔符没有指定间隔的位数,也就是说,可以每三位添加一个分隔符,也可以每一位、每两位、每四位添加一个。 + +```javascript +123_00 === 12_300 // true + +12345_00 === 123_4500 // true +12345_00 === 1_234_500 // true +``` + +小数和科学计数法也可以使用数值分隔符。 + +```javascript +// 小数 +0.000_001 +// 科学计数法 +1e10_000 +``` + +数值分隔符有几个使用注意点。 + +- 不能在数值的最前面(leading)或最后面(trailing)。 +- 不能两个或两个以上的分隔符连在一起。 +- 小数点的前后不能有分隔符。 +- 科学计数法里面,表示指数的`e`或`E`前后不能有分隔符。 + +下面的写法都会报错。 + +```javascript +// 全部报错 +3_.141 +3._141 +1_e12 +1e_12 +123__456 +_1464301 +1464301_ +``` + +除了十进制,其他进制的数值也可以使用分隔符。 + +```javascript +// 二进制 +0b1010_0001_1000_0101 +// 十六进制 +0xA0_B0_C0 +``` + +注意,分隔符不能紧跟着进制的前缀`0b`、`0B`、`0o`、`0O`、`0x`、`0X`。 + +```javascript +// 报错 +0_b111111000 +0b_111111000 +``` + +下面三个将字符串转成数值的函数,不支持数值分隔符。主要原因是提案的设计者认为,数值分隔符主要是为了编码时书写数值的方便,而不是为了处理外部输入的数据。 + +- Number() +- parseInt() +- parseFloat() + +```javascript +Number('123_456') // NaN +parseInt('123_456') // 123 +``` + +## Integer 数据类型 + +### 简介 + +JavaScript 所有数字都保存成 64 位浮点数,这决定了整数的精确程度只能到 53 个二进制位。大于这个范围的整数,JavaScript 是无法精确表示的,这使得 JavaScript 不适合进行科学和金融方面的精确计算。 + +现在有一个[提案](https://github.com/tc39/proposal-bigint),引入了新的数据类型 Integer(整数),来解决这个问题。整数类型的数据只用来表示整数,没有位数的限制,任何位数的整数都可以精确表示。 + +为了与 Number 类型区别,Integer 类型的数据必须使用后缀`n`表示。 + +```javascript +1n + 2n // 3n +``` + +二进制、八进制、十六进制的表示法,都要加上后缀`n`。 + +```javascript +0b1101n // 二进制 +0o777n // 八进制 +0xFFn // 十六进制 +``` + +`typeof`运算符对于 Integer 类型的数据返回`integer`。 + +```javascript +typeof 123n +// 'integer' +``` + +JavaScript 原生提供`Integer`对象,用来生成 Integer 类型的数值。转换规则基本与`Number()`一致。 + +```javascript +Integer(123) // 123n +Integer('123') // 123n +Integer(false) // 0n +Integer(true) // 1n +``` + +以下的用法会报错。 + +```javascript +new Integer() // TypeError +Integer(undefined) //TypeError +Integer(null) // TypeError +Integer('123n') // SyntaxError +Integer('abc') // SyntaxError +``` + +### 运算 + +在数学运算方面,Integer 类型的`+`、`-`、`*`和`**`这四个二元运算符,与 Number 类型的行为一致。除法运算`/`会舍去小数部分,返回一个整数。 + +```javascript +9n / 5n +// 1n +``` + +几乎所有的 Number 运算符都可以用在 Integer,但是有两个除外:不带符号的右移位运算符`>>>`和一元的求正运算符`+`,使用时会报错。前者是因为`>>>`要求最高位补 0,但是 Integer 类型没有最高位,导致这个运算符无意义。后者是因为一元运算符`+`在 asm.js 里面总是返回 Number 类型或者报错。 + +Integer 类型不能与 Number 类型进行混合运算。 + +```javascript +1n + 1 +// 报错 +``` + +这是因为无论返回的是 Integer 或 Number,都会导致丢失信息。比如`(2n**53n + 1n) + 0.5`这个表达式,如果返回 Integer 类型,`0.5`这个小数部分会丢失;如果返回 Number 类型,会超过 53 位精确数字,精度下降。 + +相等运算符(`==`)会改变数据类型,也是不允许混合使用。 + +```javascript +0n == 0 +// 报错 TypeError + +0n == false +// 报错 TypeError +``` + +精确相等运算符(`===`)不会改变数据类型,因此可以混合使用。 + +```javascript +0n === 0 +// false +``` + +## Math.signbit() + +`Math.sign()`用来判断一个值的正负,但是如果参数是`-0`,它会返回`-0`。 + +```javascript +Math.sign(-0) // -0 +``` + +这导致对于判断符号位的正负,`Math.sign()`不是很有用。JavaScript 内部使用 64 位浮点数(国际标准 IEEE 754)表示数值,IEEE 754 规定第一位是符号位,`0`表示正数,`1`表示负数。所以会有两种零,`+0`是符号位为`0`时的零值,`-0`是符号位为`1`时的零值。实际编程中,判断一个值是`+0`还是`-0`非常麻烦,因为它们是相等的。 + +```javascript ++0 === -0 // true +``` + +目前,有一个[提案](http://jfbastien.github.io/papers/Math.signbit.html),引入了`Math.signbit()`方法判断一个数的符号位是否设置了。 + +```javascript +Math.signbit(2) //false +Math.signbit(-2) //true +Math.signbit(0) //false +Math.signbit(-0) //true +``` + +可以看到,该方法正确返回了`-0`的符号位是设置了的。 + +该方法的算法如下。 + +- 如果参数是`NaN`,返回`false` +- 如果参数是`-0`,返回`true` +- 如果参数是负值,返回`true` +- 其他情况返回`false` + From e59bd7b537e7a725d5bbec22d8239c69e4c7fef9 Mon Sep 17 00:00:00 2001 From: zhangbao Date: Fri, 9 Feb 2018 12:56:15 +0800 Subject: [PATCH 093/595] Fix typo. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit charAt方法返回的,并非是 UTF-16 编码的第一个字节 而是 期望返回的是用2个字节表示的字符。 --- docs/string.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/string.md b/docs/string.md index 770ed08bc..d576c10f9 100644 --- a/docs/string.md +++ b/docs/string.md @@ -178,7 +178,7 @@ ES5 对字符串对象提供`charAt`方法,返回字符串给定位置的字 '𠮷'.charAt(0) // "\uD842" ``` -上面代码中,`charAt`方法返回的是 UTF-16 编码的第一个字节,实际上是无法显示的。 +上面代码中的第二条语句,`charAt`方法期望返回的是用2个字节表示的字符,但汉字“𠮷”占用了4个字节,`charAt(0)` 表示获取这4个字节中的前2个字节,很显然,这是无法正常显示的。 目前,有一个提案,提出字符串实例的`at`方法,可以识别 Unicode 编号大于`0xFFFF`的字符,返回正确的字符。 From c64177a5dc87064e6d8fdf5b33f25b2b034a41f6 Mon Sep 17 00:00:00 2001 From: zhangbao Date: Fri, 9 Feb 2018 13:26:01 +0800 Subject: [PATCH 094/595] Update string.md --- docs/string.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/string.md b/docs/string.md index d576c10f9..139920a92 100644 --- a/docs/string.md +++ b/docs/string.md @@ -178,7 +178,7 @@ ES5 对字符串对象提供`charAt`方法,返回字符串给定位置的字 '𠮷'.charAt(0) // "\uD842" ``` -上面代码中的第二条语句,`charAt`方法期望返回的是用2个字节表示的字符,但汉字“𠮷”占用了4个字节,`charAt(0)` 表示获取这4个字节中的前2个字节,很显然,这是无法正常显示的。 +上面代码中的第二条语句,`charAt`方法期望返回的是用2个字节表示的字符,但汉字“𠮷”占用了4个字节,`charAt(0)`表示获取这4个字节中的前2个字节,很显然,这是无法正常显示的。 目前,有一个提案,提出字符串实例的`at`方法,可以识别 Unicode 编号大于`0xFFFF`的字符,返回正确的字符。 From b87ae6e7038d8f58b7a33b6f0fe6961db0d85895 Mon Sep 17 00:00:00 2001 From: ruanyf Date: Fri, 9 Feb 2018 16:04:19 +0800 Subject: [PATCH 095/595] docs(proposals): edit proposals --- docs/proposals.md | 110 ++++++++++++++++++++++++++++++++++++---------- docs/regex.md | 3 ++ 2 files changed, 89 insertions(+), 24 deletions(-) diff --git a/docs/proposals.md b/docs/proposals.md index 6d0b2dedc..7de9e231a 100644 --- a/docs/proposals.md +++ b/docs/proposals.md @@ -464,21 +464,30 @@ Number('123_456') // NaN parseInt('123_456') // 123 ``` -## Integer 数据类型 +## BigInt 数据类型 ### 简介 -JavaScript 所有数字都保存成 64 位浮点数,这决定了整数的精确程度只能到 53 个二进制位。大于这个范围的整数,JavaScript 是无法精确表示的,这使得 JavaScript 不适合进行科学和金融方面的精确计算。 +JavaScript 所有数字都保存成 64 位浮点数,这给数值的表示带来了两大限制。一是数值的精度只能到 53 个二进制位(相当于 16 个十进制位),大于这个范围的整数,JavaScript 是无法精确表示的,这使得 JavaScript 不适合进行科学和金融方面的精确计算。二是大于或等于2的1024次方的数值,JavaScript 无法表示,会返回`Infinite`。 -现在有一个[提案](https://github.com/tc39/proposal-bigint),引入了新的数据类型 Integer(整数),来解决这个问题。整数类型的数据只用来表示整数,没有位数的限制,任何位数的整数都可以精确表示。 +```javascript +// 超过 53 个二进制位的数值,无法保持精度 +Math.pow(2, 53) === Math.pow(2, 53) + 1 // true + +// 超过 2 的 1024 次方的数值,无法表示 +Math.pow(2, 1024) // Infinity +``` + +现在有一个[提案](https://github.com/tc39/proposal-bigint),引入了一种新的数据类型 BigInt(大整数),来解决这个问题。BigInt 只用来表示整数,没有位数的限制,任何位数的整数都可以精确表示。 -为了与 Number 类型区别,Integer 类型的数据必须使用后缀`n`表示。 +为了与 Number 类型区别,BigInt 类型的数据必须使用后缀`n`表示。 ```javascript +1234n 1n + 2n // 3n ``` -二进制、八进制、十六进制的表示法,都要加上后缀`n`。 +BigInt 同样可以使用各种进制表示,都要加上后缀`n`。 ```javascript 0b1101n // 二进制 @@ -486,51 +495,86 @@ JavaScript 所有数字都保存成 64 位浮点数,这决定了整数的精 0xFFn // 十六进制 ``` -`typeof`运算符对于 Integer 类型的数据返回`integer`。 +`typeof`运算符对于 BigInt 类型的数据返回`bigint`。 ```javascript -typeof 123n -// 'integer' +typeof 123n // 'BigInt' ``` -JavaScript 原生提供`Integer`对象,用来生成 Integer 类型的数值。转换规则基本与`Number()`一致。 +### BigInt 对象 + +JavaScript 原生提供`BigInt`对象,可以用作构造函数生成 BitInt 类型的数值。转换规则基本与`Number()`一致,将别的类型的值转为 BigInt。 ```javascript -Integer(123) // 123n -Integer('123') // 123n -Integer(false) // 0n -Integer(true) // 1n +BigInt(123) // 123n +BigInt('123') // 123n +BitInt(false) // 0n +BitInt(true) // 1n ``` -以下的用法会报错。 +`BitInt`构造函数必须有参数,而且参数必须可以正常转为数值,下面的用法都会报错。 ```javascript -new Integer() // TypeError -Integer(undefined) //TypeError -Integer(null) // TypeError -Integer('123n') // SyntaxError -Integer('abc') // SyntaxError +new BitInt() // TypeError +BigInt(undefined) //TypeError +BigInt(null) // TypeError +BigInt('123n') // SyntaxError +BigInt('abc') // SyntaxError ``` +上面代码中,尤其值得注意字符串`123n`无法解析成 Number 类型,所以会报错。 + +BigInt 对象继承了 Object 提供的实例方法。 + +- `BigInt.prototype.toLocaleString()` +- `BigInt.prototype.toString()` +- `BigInt.prototype.valueOf()` + +此外,还提供了三个静态方法。 + +- `BigInt.asUintN(width, BigInt)`: 对给定的大整数,返回 0 到 2width - 1 之间的大整数形式。 +- `BigInt.asIntN(width, BigInt)`:对给定的大整数,返回 -2width - 1 到 2width - 1 - 1 之间的大整数形式。 +- `BigInt.parseInt(string[, radix])`:近似于`Number.parseInt`,将一个字符串转换成指定进制的大整数。 + +```javascript +// 将一个大整数转为 64 位整数的形式 +const int64a = BigInt.asUintN(64, 12345n); + +// Number.parseInt 与 BigInt.parseInt 的对比 +Number.parseInt('9007199254740993', 10) +// 9007199254740992 +BigInt.parseInt('9007199254740993', 10) +// 9007199254740993n +``` + +上面代码中,由于有效数字超出了最大限度,`Number.parseInt`方法返回的结果是不精确的,而`BigInt.parseInt`方法正确返回了对应的大整数。 + +对于二进制数组,BigInt 新增了两个类型`BigUint64Array`和`BigInt64Array`,这两种数据类型返回的都是大整数。`DataView`对象的实例方法`DataView.prototype.getBigInt64`和`DataView.prototype.getBigUint64`,返回的也是大整数。 + ### 运算 -在数学运算方面,Integer 类型的`+`、`-`、`*`和`**`这四个二元运算符,与 Number 类型的行为一致。除法运算`/`会舍去小数部分,返回一个整数。 +数学运算方面,BigInt 类型的`+`、`-`、`*`和`**`这四个二元运算符,与 Number 类型的行为一致。除法运算`/`会舍去小数部分,返回一个整数。 ```javascript 9n / 5n // 1n ``` -几乎所有的 Number 运算符都可以用在 Integer,但是有两个除外:不带符号的右移位运算符`>>>`和一元的求正运算符`+`,使用时会报错。前者是因为`>>>`要求最高位补 0,但是 Integer 类型没有最高位,导致这个运算符无意义。后者是因为一元运算符`+`在 asm.js 里面总是返回 Number 类型或者报错。 +几乎所有的 Number 运算符都可以用在 BigInt,但是有两个除外:不带符号的右移位运算符`>>>`和一元的求正运算符`+`,使用时会报错。前者是因为`>>>`运算符是不带符号的,但是 BigInt 总是带有符号的,导致该运算无意义,完全等同于右移运算符`>>`。后者是因为一元运算符`+`在 asm.js 里面总是返回 Number 类型,为了不破坏 asm.js 就规定`+1n`会报错。 Integer 类型不能与 Number 类型进行混合运算。 ```javascript -1n + 1 -// 报错 +1n + 1.3 // 报错 ``` -这是因为无论返回的是 Integer 或 Number,都会导致丢失信息。比如`(2n**53n + 1n) + 0.5`这个表达式,如果返回 Integer 类型,`0.5`这个小数部分会丢失;如果返回 Number 类型,会超过 53 位精确数字,精度下降。 +上面代码报错是因为无论返回的是 BigInt 或 Number,都会导致丢失信息。比如`(2n**53n + 1n) + 0.5`这个表达式,如果返回 BigInt 类型,`0.5`这个小数部分会丢失;如果返回 Number 类型,有效精度只能保持 53 位,导致精度下降。 + +asm.js 里面,`|0`跟在一个数值的后面会返回一个32位整数。根据不能与 Number 类型混合运算的规则,BigInt 如果与`|0`进行运算会报错。 + +```javascript +1n | 0 // 报错 +``` 相等运算符(`==`)会改变数据类型,也是不允许混合使用。 @@ -549,6 +593,24 @@ Integer 类型不能与 Number 类型进行混合运算。 // false ``` +大整数可以转为其他数据类型。 + +```javascript +Boolean(0n) // false +Boolean(1n) // true +Number(1n) // 1 +String(1n) // "1" + +!0n // true +!1n // false +``` + +大整数也可以与字符串混合运算。 + +```javascript +'' + 123n // "123" +``` + ## Math.signbit() `Math.sign()`用来判断一个值的正负,但是如果参数是`-0`,它会返回`-0`。 diff --git a/docs/regex.md b/docs/regex.md index ddc43638b..6a78a708f 100644 --- a/docs/regex.md +++ b/docs/regex.md @@ -597,7 +597,10 @@ matches ```javascript const string = 'test1test2test3'; + +// g 修饰符加不加都可以 const regex = /t(e)(st(\d?))/g; + for (const match of string.matchAll(regex)) { console.log(match); } From de0e54caea46429bdd32cd509999ff3a709345e7 Mon Sep 17 00:00:00 2001 From: caikan Date: Sat, 10 Feb 2018 05:08:26 +0800 Subject: [PATCH 096/595] fix typo --- docs/proposals.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/proposals.md b/docs/proposals.md index 7de9e231a..9ca613f8d 100644 --- a/docs/proposals.md +++ b/docs/proposals.md @@ -195,7 +195,7 @@ const PS = eval("'\u2029'"); ### 语法 -多参数的函数有时需要绑定其中的一个或多个函数,然后返回一个新函数。 +多参数的函数有时需要绑定其中的一个或多个参数,然后返回一个新函数。 ```javascript function add(x, y) { return x + y; } @@ -214,7 +214,7 @@ const add7 = x => add(x, 7); 上面两种写法都有些冗余。其中,`bind`方法的局限更加明显,它必须提供`this`,并且只能从前到后一个个绑定参数,无法只绑定非头部的参数。 -现在有一个[提案](https://github.com/tc39/proposal-partial-application),使用绑定参数然后返回一个新函数更加容器。这叫做函数的部分执行(partial application)。 +现在有一个[提案](https://github.com/tc39/proposal-partial-application),使得绑定参数并返回一个新函数更加容易。这叫做函数的部分执行(partial application)。 ```javascript const add = (x, y) => x + y; From d569703fb4d9ada613fe8869ef50d2e3861210ba Mon Sep 17 00:00:00 2001 From: ruanyf Date: Tue, 13 Feb 2018 00:20:38 +0800 Subject: [PATCH 097/595] docs(module-loader): delete #607 --- docs/module-loader.md | 2 -- 1 file changed, 2 deletions(-) diff --git a/docs/module-loader.md b/docs/module-loader.md index 6d196ef52..4e7f86df0 100644 --- a/docs/module-loader.md +++ b/docs/module-loader.md @@ -89,8 +89,6 @@ const x = 1; console.log(x === window.x); //false console.log(this === undefined); // true - -delete x; // 句法错误,严格模式禁止删除变量 ``` 利用顶层的`this`等于`undefined`这个语法点,可以侦测当前代码是否在 ES6 模块之中。 From 01f62521de56400f6275ddc17b0979c971ee1a05 Mon Sep 17 00:00:00 2001 From: ruanyf Date: Thu, 22 Feb 2018 22:42:03 +0800 Subject: [PATCH 098/595] docs(funcion): fix double colon --- docs/function.md | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) diff --git a/docs/function.md b/docs/function.md index beaa2ac7c..5969a84e0 100644 --- a/docs/function.md +++ b/docs/function.md @@ -944,23 +944,15 @@ let log = ::console.log; var log = console.log.bind(console); ``` -双冒号运算符的运算结果,还是一个函数,因此可以采用链式写法。 +如果双冒号运算符的运算结果,还是一个对象,就可以采用链式写法。 ```javascript -// 例一 import { map, takeWhile, forEach } from "iterlib"; getPlayers() ::map(x => x.character()) ::takeWhile(x => x.strength > 100) ::forEach(x => console.log(x)); - -// 例二 -let { find, html } = jake; - -document.querySelectorAll("div.myClass") -::find("p") -::html("hahaha"); ``` ## 尾调用优化 From 9f82a677ce1d12f527fe43db7b4aa7d7a89c6f40 Mon Sep 17 00:00:00 2001 From: ruanyf Date: Fri, 23 Feb 2018 12:38:44 +0800 Subject: [PATCH 099/595] docs(decorator): fix typo --- docs/decorator.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/decorator.md b/docs/decorator.md index 7791ace26..52dffceb3 100644 --- a/docs/decorator.md +++ b/docs/decorator.md @@ -103,7 +103,7 @@ let obj = new MyClass(); obj.foo() // 'foo' ``` -上面代码通过修饰器`mixins`,把`Foo`类的方法添加到了`MyClass`的实例上面。可以用`Object.assign()`模拟这个功能。 +上面代码通过修饰器`mixins`,把`Foo`对象的方法添加到了`MyClass`的实例上面。可以用`Object.assign()`模拟这个功能。 ```javascript const Foo = { From c7b5077af4755e236dbb1931c2b5066932867b3f Mon Sep 17 00:00:00 2001 From: cntanglijun <869058216@qq.com> Date: Sun, 25 Feb 2018 00:48:27 +0800 Subject: [PATCH 100/595] =?UTF-8?q?=E5=B0=91=E4=BA=86=E4=B8=80=E4=B8=AA"?= =?UTF-8?q?=E4=B8=AA"=E5=AD=97?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/generator-async.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/generator-async.md b/docs/generator-async.md index fd5200953..53966a342 100644 --- a/docs/generator-async.md +++ b/docs/generator-async.md @@ -151,7 +151,7 @@ g.next() // { value: 3, done: false } g.next(2) // { value: 2, done: true } ``` -上面代码中,第一`next`方法的`value`属性,返回表达式`x + 2`的值`3`。第二个`next`方法带有参数`2`,这个参数可以传入 Generator 函数,作为上个阶段异步任务的返回结果,被函数体内的变量`y`接收。因此,这一步的`value`属性,返回的就是`2`(变量`y`的值)。 +上面代码中,第一个`next`方法的`value`属性,返回表达式`x + 2`的值`3`。第二个`next`方法带有参数`2`,这个参数可以传入 Generator 函数,作为上个阶段异步任务的返回结果,被函数体内的变量`y`接收。因此,这一步的`value`属性,返回的就是`2`(变量`y`的值)。 Generator 函数内部还可以部署错误处理代码,捕获函数体外抛出的错误。 From df6cc1a71de2b2c11d389709ba9bc38bdce30159 Mon Sep 17 00:00:00 2001 From: yuri Date: Sun, 25 Feb 2018 14:16:22 +0800 Subject: [PATCH 101/595] =?UTF-8?q?=E4=BF=AE=E6=AD=A3=E7=AE=A1=E9=81=93?= =?UTF-8?q?=E8=BF=90=E7=AE=97=E7=AC=A6=E6=8F=90=E6=A1=88=E5=9C=B0=E5=9D=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/proposals.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/proposals.md b/docs/proposals.md index 9ca613f8d..f28f9e61c 100644 --- a/docs/proposals.md +++ b/docs/proposals.md @@ -319,7 +319,7 @@ g(1, 2, 3); // [1, 2, 3, 9, 1, 2, 3] ## 管道运算符 -Unix 操作系统有一个管道机制(pipeline),可以把前一个操作的值传给后一个操作。这个机制非常有用,使得简单的操作可以组合成为复杂的操作。许多语言都有管道的实现,现在有一个[提案](https://github.com/tc39/proposal-partial-application),让 JavaScript 也拥有管道机制。 +Unix 操作系统有一个管道机制(pipeline),可以把前一个操作的值传给后一个操作。这个机制非常有用,使得简单的操作可以组合成为复杂的操作。许多语言都有管道的实现,现在有一个[提案](https://github.com/tc39/proposal-pipeline-operator),让 JavaScript 也拥有管道机制。 JavaScript 的管道是一个运算符,写作`|>`。它的左边是一个表达式,右边是一个函数。管道运算符把左边表达式的值,传入右边的函数进行求值。 From 78d9b668dda9d7b4d8caf3da724596311f0007a9 Mon Sep 17 00:00:00 2001 From: yuri Date: Sun, 25 Feb 2018 14:24:17 +0800 Subject: [PATCH 102/595] =?UTF-8?q?Null=20=E4=BC=A0=E5=AF=BC=E8=BF=90?= =?UTF-8?q?=E7=AE=97=E7=AC=A6=E6=8F=90=E6=A1=88=E5=9C=B0=E5=9D=80=E6=9B=BF?= =?UTF-8?q?=E6=8D=A2=E4=B8=BAtc39=E5=AE=98=E6=96=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/proposals.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/proposals.md b/docs/proposals.md index 9ca613f8d..97ad41675 100644 --- a/docs/proposals.md +++ b/docs/proposals.md @@ -129,7 +129,7 @@ const firstName = (message && message.body.user.firstName) || 'default'; ``` -这样的层层判断非常麻烦,因此现在有一个[提案](https://github.com/claudepache/es-optional-chaining),引入了“Null 传导运算符”(null propagation operator)`?.`,简化上面的写法。 +这样的层层判断非常麻烦,因此现在有一个[提案](https://github.com/tc39/proposal-optional-chaining),引入了“Null 传导运算符”(null propagation operator)`?.`,简化上面的写法。 ```javascript const firstName = message?.body?.user?.firstName || 'default'; From be47d277ac5ae9b1a08891ca22fb250262d3166d Mon Sep 17 00:00:00 2001 From: ruanyf Date: Mon, 26 Feb 2018 14:35:53 +0800 Subject: [PATCH 103/595] docs(proposals): edit option chaining operator --- docs/proposals.md | 81 +++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 68 insertions(+), 13 deletions(-) diff --git a/docs/proposals.md b/docs/proposals.md index 9ca613f8d..6c2ac6b98 100644 --- a/docs/proposals.md +++ b/docs/proposals.md @@ -118,7 +118,7 @@ class Product { 语法上,`throw`表达式里面的`throw`不再是一个命令,而是一个运算符。为了避免与`throw`命令混淆,规定`throw`出现在行首,一律解释为`throw`语句,而不是`throw`表达式。 -## Null 传导运算符 +## 链判断运算符 编程实务中,如果读取对象内部的某个属性,往往需要判断一下该对象是否存在。比如,要读取`message.body.user.firstName`,安全的写法是写成下面这样。 @@ -129,38 +129,93 @@ const firstName = (message && message.body.user.firstName) || 'default'; ``` -这样的层层判断非常麻烦,因此现在有一个[提案](https://github.com/claudepache/es-optional-chaining),引入了“Null 传导运算符”(null propagation operator)`?.`,简化上面的写法。 +这样的层层判断非常麻烦,因此现在有一个[提案](https://github.com/claudepache/es-optional-chaining),引入了“链判断运算符”(optional chaining operator)`?.`,简化上面的写法。 ```javascript const firstName = message?.body?.user?.firstName || 'default'; ``` -上面代码有三个`?.`运算符,只要其中一个返回`null`或`undefined`,就不再往下运算,而是返回`undefined`。 +上面代码有三个`?.`运算符,直接在链式调用的时候判断,左侧的对象是否为`null`或`undefined`。如果是的,就不再往下运算,而是返回`undefined`。 -“Null 传导运算符”有四种用法。 +链判断运算符号有三种用法。 - `obj?.prop` // 读取对象属性 - `obj?.[expr]` // 同上 - `func?.(...args)` // 函数或对象方法的调用 -- `new C?.(...args)` // 构造函数的调用 -传导运算符之所以写成`obj?.prop`,而不是`obj?prop`,是为了方便编译器能够区分三元运算符`?:`(比如`obj?prop:123`)。 +下面是判断函数是否存在的例子。 + +```javascript +iterator.return?.() +``` + +上面代码中,`iterator.return`如果有定义,就会调用该方法,否则直接返回`undefined`。 下面是更多的例子。 ```javascript -// 如果 a 是 null 或 undefined, 返回 undefined -// 否则返回 a.b.c().d -a?.b.c().d +a?.b +// 等同于 +a == null ? undefined : a.b + +a?.[x] +// 等同于 +a == null ? undefined : a[x] + +a?.b() +// 等同于 +a == null ? undefined : a.b() + +a?.() +// 等同于 +a == null ? undefined : a() +``` + +使用这个运算符,有几个注意点。 + +(1)短路机制 + +```javascript +a?.[++x] +// 等同于 +a == null ? undefined : a[++x] +``` -// 如果 a 是 null 或 undefined,下面的语句不产生任何效果 -// 否则执行 a.b = 42 -a?.b = 42 +上面代码中,如果`a`是`undefined`或`null`,那么`x`不会进行递增运算。也就是说,链判断运算符一旦为真,右侧的表达式就不再求值。 -// 如果 a 是 null 或 undefined,下面的语句不产生任何效果 +(2)delete 运算符 + +```javascript delete a?.b +// 等同于 +a == null ? undefined : delete a.b +``` + +上面代码中,如果`a`是`undefined`或`null`,会直接返回`undefined`,而不会进行`delete`运算。 + +(3)报错场合 + +以下写法是禁止,会报错。 + +```javascript +// 构造函数判断 +new a?.() + +// 运算符右侧是模板字符串 +a?.`{b}` + +// 链判断运算符前后有构造函数或模板字符串 +new a?.b() +a?.b`{c}` + +// 链运算符用于赋值运算符左侧 +a?.b = c ``` +(4)右侧不得为十进制数值 + +为了保证兼容以前的代码,允许`foo?.3:0`被解析成`foo ? .3 : 0`,因此规定如果`?.`后面紧跟一个十进制数字,那么`?.`不再被看成是一个完整的运算符,而会按照三元运算符进行处理,也就是说,那个小数点会归属于后面的十进制数字,形成一个小数。 + ## 直接输入 U+2028 和 U+2029 JavaScript 字符串允许直接输入字符,以及输入字符的转义形式。举例来说,“中”的 Unicode 码点是 U+4e2d,你可以直接在字符串里面输入这个汉字,也可以输入它的转义形式`\u4e2d`,两者是等价的。 From 52e67f71da82dd4bee54bc38682ffacdf7e0024f Mon Sep 17 00:00:00 2001 From: ruanyf Date: Mon, 26 Feb 2018 15:20:30 +0800 Subject: [PATCH 104/595] docs(map): fix map #614 --- docs/set-map.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/docs/set-map.md b/docs/set-map.md index 27f109e13..bcbd5370a 100644 --- a/docs/set-map.md +++ b/docs/set-map.md @@ -805,7 +805,7 @@ new Map([ **(3)Map 转为对象** -如果所有 Map 的键都是字符串,它可以转为对象。 +如果所有 Map 的键都是字符串,它可以无损地转为对象。 ```javascript function strMapToObj(strMap) { @@ -823,6 +823,8 @@ strMapToObj(myMap) // { yes: true, no: false } ``` +如果有非字符串的键名,那么这个键名会被转成字符串,再作为对象的键名。 + **(4)对象转为 Map** ```javascript From 50bd8d09d2fe27f747e848c0320c7ea78e1267a8 Mon Sep 17 00:00:00 2001 From: ruanyf Date: Mon, 26 Feb 2018 15:22:47 +0800 Subject: [PATCH 105/595] docs(proxy): fix proxy #616 --- docs/proxy.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/proxy.md b/docs/proxy.md index e71e40d48..bacb8ac01 100644 --- a/docs/proxy.md +++ b/docs/proxy.md @@ -209,7 +209,7 @@ let arr = createArray('a', 'b', 'c'); arr[-1] // c ``` -上面代码中,数组的位置参数是`-1`,就会输出数组的倒数最后一个成员。 +上面代码中,数组的位置参数是`-1`,就会输出数组的倒数第一个成员。 利用 Proxy,可以将读取属性的操作(`get`),转变为执行某个函数,从而实现属性的链式操作。 From 69a86e75c54ae6a8cda20ff0c854f8c24ebc3324 Mon Sep 17 00:00:00 2001 From: xianshenglu Date: Tue, 27 Feb 2018 09:28:35 +0800 Subject: [PATCH 106/595] =?UTF-8?q?=E4=B8=A5=E8=B0=A8=E5=8C=96=20\S=20?= =?UTF-8?q?=E7=9A=84=E6=8F=8F=E8=BF=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit \S 匹配所有非空白字符 而非所有不是空格的字符 --- docs/regex.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/regex.md b/docs/regex.md index 6a78a708f..f64421b4c 100644 --- a/docs/regex.md +++ b/docs/regex.md @@ -105,7 +105,7 @@ ES6 新增了使用大括号表示 Unicode 字符,这种表示法在正则表 /^\S$/u.test('𠮷') // true ``` -上面代码的`\S`是预定义模式,匹配所有不是空格的字符。只有加了`u`修饰符,它才能正确匹配码点大于`0xFFFF`的 Unicode 字符。 +上面代码的`\S`是预定义模式,匹配所有非空白字符。只有加了`u`修饰符,它才能正确匹配码点大于`0xFFFF`的 Unicode 字符。 利用这一点,可以写出一个正确返回字符串长度的函数。 From 53389645e7790b0d5009d9e6fa32fe31246e56d6 Mon Sep 17 00:00:00 2001 From: ruanyf Date: Thu, 1 Mar 2018 13:30:46 +0800 Subject: [PATCH 107/595] docs(promise): edit promise --- docs/promise.md | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/docs/promise.md b/docs/promise.md index 4793c58b3..15b1ca780 100644 --- a/docs/promise.md +++ b/docs/promise.md @@ -681,8 +681,10 @@ const p = Promise.race([ setTimeout(() => reject(new Error('request timeout')), 5000) }) ]); -p.then(response => console.log(response)); -p.catch(error => console.log(error)); + +p +.then(console.log) +.catch(console.error); ``` 上面代码中,如果 5 秒之内`fetch`方法无法返回结果,变量`p`的状态就会变为`rejected`,从而触发`catch`方法指定的回调函数。 From b4f12261780c5f446fd12bbb3a42a3c95657b966 Mon Sep 17 00:00:00 2001 From: ruanyf Date: Fri, 2 Mar 2018 12:42:37 +0800 Subject: [PATCH 108/595] docs(regex): edit regex --- docs/regex.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/regex.md b/docs/regex.md index 6a78a708f..8de7fc724 100644 --- a/docs/regex.md +++ b/docs/regex.md @@ -182,7 +182,7 @@ match.index // 3 REGEX.lastIndex // 4 // 4号位开始匹配失败 -REGEX.exec('xaxa') // null +REGEX.exec('xaya') // null ``` 上面代码中,`lastIndex`属性指定每次搜索的开始位置,`g`修饰符从这个位置开始向后搜索,直到发现匹配为止。 @@ -202,7 +202,7 @@ REGEX.exec('xaya') // null REGEX.lastIndex = 3; // 3号位置是粘连,匹配成功 -const match = REGEX.exec('xaxa'); +const match = REGEX.exec('xaya'); match.index // 3 REGEX.lastIndex // 4 ``` From 3c7e8a6365723ee7aa4e5ca67c4edb8c0cd4e5a4 Mon Sep 17 00:00:00 2001 From: ruanyf Date: Fri, 2 Mar 2018 15:44:37 +0800 Subject: [PATCH 109/595] docs(module-extends): edit extends --- docs/class-extends.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/class-extends.md b/docs/class-extends.md index 25d94e466..b65d11485 100644 --- a/docs/class-extends.md +++ b/docs/class-extends.md @@ -389,7 +389,7 @@ class B { // B 的实例继承 A 的实例 Object.setPrototypeOf(B.prototype, A.prototype); -// B 的实例继承 A 的静态属性 +// B 继承 A 的静态属性 Object.setPrototypeOf(B, A); const b = new B(); From 3d8af6149cf3d3f44fb03c1b126de142d8b451f2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=8E=8B=E7=A6=8F=E5=AE=97?= Date: Sat, 3 Mar 2018 20:35:02 +0800 Subject: [PATCH 110/595] =?UTF-8?q?=E7=BB=99=E5=BD=93=E5=89=8D=E5=AF=B9?= =?UTF-8?q?=E8=B1=A1=E5=8A=A0=E4=B8=8A=E5=90=8C=E5=90=8D=E5=B1=9E=E4=BA=8E?= =?UTF-8?q?=EF=BC=8C=E5=BD=A2=E6=88=90=E5=AF=B9=E6=AF=94?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 给当前对象加上同名属于,由于值不同,会有更直观的对比~ 同时,也和最后一个例子有呼应,方便初学者理解 --- docs/object.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/object.md b/docs/object.md index c5249725a..d6ef05138 100644 --- a/docs/object.md +++ b/docs/object.md @@ -1032,6 +1032,7 @@ const proto = { }; const obj = { + foo: 'world', find() { return super.foo; } From fecda8655d4f31242a89fc69d9701a9445fa2c5c Mon Sep 17 00:00:00 2001 From: TinaC Date: Mon, 5 Mar 2018 10:26:07 +0800 Subject: [PATCH 111/595] fix typo --- docs/module-loader.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/module-loader.md b/docs/module-loader.md index 4e7f86df0..aa3b23edc 100644 --- a/docs/module-loader.md +++ b/docs/module-loader.md @@ -631,7 +631,7 @@ ReferenceError: foo is not defined 上面代码中,执行`a.mjs`以后会报错,`foo`变量未定义,这是为什么? -让我们一行行来看,ES6 循环加载是怎么处理的。首先,执行`a.mjs`以后,引擎发现它加载了`b.mjs`,因此会优先执行`b.mjs`,然后再执行`a.js`。接着,执行`b.mjs`的时候,已知它从`a.mjs`输入了`foo`接口,这时不会去执行`a.mjs`,而是认为这个接口已经存在了,继续往下执行。执行到第三行`console.log(foo)`的时候,才发现这个接口根本没定义,因此报错。 +让我们一行行来看,ES6 循环加载是怎么处理的。首先,执行`a.mjs`以后,引擎发现它加载了`b.mjs`,因此会优先执行`b.mjs`,然后再执行`a.mjs`。接着,执行`b.mjs`的时候,已知它从`a.mjs`输入了`foo`接口,这时不会去执行`a.mjs`,而是认为这个接口已经存在了,继续往下执行。执行到第三行`console.log(foo)`的时候,才发现这个接口根本没定义,因此报错。 解决这个问题的方法,就是让`b.mjs`运行的时候,`foo`已经有定义了。这可以通过将`foo`写成函数来解决。 From 3cf3f8fdb6548e66ee8c3234cd44234761ae5372 Mon Sep 17 00:00:00 2001 From: ruanyf Date: Tue, 6 Mar 2018 12:12:55 +0800 Subject: [PATCH 112/595] =?UTF-8?q?docs(async):=20fix=20=E5=BC=82=E6=AD=A5?= =?UTF-8?q?=E9=81=8D=E5=8E=86=E5=99=A8=E5=87=BD=E6=95=B0=20#622?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/async.md | 50 +++++++++++++++++++++++++++++++------------------- 1 file changed, 31 insertions(+), 19 deletions(-) diff --git a/docs/async.md b/docs/async.md index d922b4197..481315200 100644 --- a/docs/async.md +++ b/docs/async.md @@ -733,10 +733,14 @@ console.log(v1, v2); // a b 另一种用法是一次性调用所有的`next`方法,然后`await`最后一步操作。 ```javascript -const writer = openFile('someFile.txt'); -writer.next('hello'); -writer.next('world'); -await writer.return(); +async function runner() { + const writer = openFile('someFile.txt'); + writer.next('hello'); + writer.next('world'); + await writer.return(); +} + +runner(); ``` ### for await...of @@ -836,7 +840,7 @@ async function* map(iterable, func) { } ``` -上面代码中,可以看到有了异步遍历器以后,同步 Generator 函数和异步 Generator 函数的写法基本上是一致的。 +上面代码中,`map`是一个 Generator 函数,第一个参数是可遍历对象`iterable`,第二个参数是一个回调函数`func`。`map`的作用是将`iterable`每一步返回的值,使用`func`进行处理。上面有两个版本的`map`,前一个处理同步遍历器,后一个处理异步遍历器,可以看到两个版本的写法基本上是一致的。 下面是另一个异步 Generator 函数的例子。 @@ -854,7 +858,7 @@ async function* readLines(path) { } ``` -上面代码中,异步操作前面使用`await`关键字标明,即`await`后面的操作,应该返回 Promise 对象。凡是使用`yield`关键字的地方,就是`next`方法的停下来的地方,它后面的表达式的值(即`await file.readLine()`的值),会作为`next()`返回对象的`value`属性,这一点是与同步 Generator 函数一致的。 +上面代码中,异步操作前面使用`await`关键字标明,即`await`后面的操作,应该返回 Promise 对象。凡是使用`yield`关键字的地方,就是`next`方法停下来的地方,它后面的表达式的值(即`await file.readLine()`的值),会作为`next()`返回对象的`value`属性,这一点是与同步 Generator 函数一致的。 异步 Generator 函数内部,能够同时使用`await`和`yield`命令。可以这样理解,`await`命令用于将外部操作产生的值输入函数内部,`yield`命令用于将函数内部的值输出。 @@ -881,42 +885,50 @@ async function* prefixLines(asyncIterable) { 异步 Generator 函数的返回值是一个异步 Iterator,即每次调用它的`next`方法,会返回一个 Promise 对象,也就是说,跟在`yield`命令后面的,应该是一个 Promise 对象。 ```javascript +function fetchRandom() { + const url = 'https://www.random.org/decimal-fractions/' + + '?num=1&dec=10&col=1&format=plain&rnd=new'; + return fetch(url); +} + async function* asyncGenerator() { console.log('Start'); - const result = await doSomethingAsync(); // (A) - yield 'Result: '+ result; // (B) + const result = await fetchRandom(); // (A) + yield 'Result: ' + await result.text(); // (B) console.log('Done'); } const ag = asyncGenerator(); -ag.next().then({value, done} => { - // ... +ag.next().then(({value, done}) => { + console.log(value); }) ``` -上面代码中,`ag`是`asyncGenerator`函数返回的异步 Iterator 对象。调用`ag.next()`以后,`asyncGenerator`函数内部的执行顺序如下。 +上面代码中,`ag`是`asyncGenerator`函数返回的异步遍历器对象。调用`ag.next()`以后,上面代码的执行顺序如下。 -1. 打印出`Start`。 -2. `await`命令返回一个 Promise 对象,但是程序不会停在这里,继续往下执行。 -3. 程序在`B`处暂停执行,`yield`命令立刻返回一个 Promise 对象,该对象就是`ag.next()`的返回值。 -4. `A`处`await`命令后面的那个 Promise 对象 resolved,产生的值放入`result`变量。 -5. `B`处的 Promise 对象 resolved,`then`方法指定的回调函数开始执行,该函数的参数是一个对象,`value`的值是表达式`'Result: ' + result`的值,`done`属性的值是`false`。 +1. `ag.next()`立刻返回一个 Promise 对象。 +1. `asyncGenerator`函数开始执行,打印出`Start`。 +1. `await`命令返回一个 Promise 对象,`asyncGenerator`函数停在这里。 +1. A 处变成 fulfilled 状态,产生的值放入`result`变量,`asyncGenerator`函数继续往下执行。 +1. 函数在 B 处的`yield`暂停执行,一旦`yield`命令取到值,`ag.next()`返回的那个 Promise 对象变成 fulfilled 状态。 +1. `ag.next()`后面的`then`方法指定的回调函数开始执行。该回调函数的参数是一个对象`{value, done}`,其中`value`的值是`yield`命令后面的那个表达式的值,`done`的值是`false`。 A 和 B 两行的作用类似于下面的代码。 ```javascript return new Promise((resolve, reject) => { - doSomethingAsync() + fetchRandom() + .then(result => result.text()) .then(result => { resolve({ - value: 'Result: '+result, + value: 'Result: ' + result, done: false, }); }); }); ``` -如果异步 Generator 函数抛出错误,会被 Promise 对象`reject`,然后抛出的错误被`catch`方法捕获。 +如果异步 Generator 函数抛出错误,会导致 Promise 对象的状态变为`reject`,然后抛出的错误被`catch`方法捕获。 ```javascript async function* asyncGenerator() { From c26d2a55e6d3d3249d07839038febfbe88a2bb11 Mon Sep 17 00:00:00 2001 From: ruanyf Date: Sat, 10 Mar 2018 16:57:58 +0800 Subject: [PATCH 113/595] docs(set): fix bug #623 --- docs/set-map.md | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/docs/set-map.md b/docs/set-map.md index bcbd5370a..9d930845b 100644 --- a/docs/set-map.md +++ b/docs/set-map.md @@ -34,21 +34,20 @@ const items = new Set([1, 2, 3, 4, 5, 5, 5, 5]); items.size // 5 // 例三 -function divs () { - return [...document.querySelectorAll('div')]; -} - -const set = new Set(divs()); +const set = new Set(document.querySelectorAll('div')); set.size // 56 // 类似于 -divs().forEach(div => set.add(div)); +const set = new Set(); +document + .querySelectorAll('div') + .forEach(div => set.add(div)); set.size // 56 ``` 上面代码中,例一和例二都是`Set`函数接受数组作为参数,例三是接受类似数组的对象作为参数。 -上面代码中,也展示了一种去除数组重复成员的方法。 +上面代码也展示了一种去除数组重复成员的方法。 ```javascript // 去除数组的重复成员 From 59f8665131e1730255294c38148e21c3fef2fb4d Mon Sep 17 00:00:00 2001 From: Chiki Date: Mon, 12 Mar 2018 10:21:11 +0800 Subject: [PATCH 114/595] Update array.md --- docs/array.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/array.md b/docs/array.md index 0352bba64..30137c236 100644 --- a/docs/array.md +++ b/docs/array.md @@ -357,7 +357,7 @@ function foo() { } ``` -上面代码中,`querySelectorAll`方法返回的是一个类似数组的对象,可以将这个对象转为真正的数组,再使用`forEach`方法。 +上面代码中,`querySelectorAll`方法返回的是一个类似数组的对象,可以将这个对象转为真正的数组,再使用`filter`方法。 只要是部署了 Iterator 接口的数据结构,`Array.from`都能将其转为数组。 From a3a76e79e2e0362ae3fcd1220dc55ffda35fd61c Mon Sep 17 00:00:00 2001 From: ruanyf Date: Thu, 15 Mar 2018 14:32:21 +0800 Subject: [PATCH 115/595] docs(class-extends): fix super --- docs/class-extends.md | 6 +++--- docs/module-loader.md | 7 +++---- docs/module.md | 33 +++++++++++++++++++++++++++++---- 3 files changed, 35 insertions(+), 11 deletions(-) diff --git a/docs/class-extends.md b/docs/class-extends.md index b65d11485..9026c6e71 100644 --- a/docs/class-extends.md +++ b/docs/class-extends.md @@ -231,7 +231,7 @@ let b = new B(); 上面代码中,属性`x`是定义在`A.prototype`上面的,所以`super.x`可以取到它的值。 -ES6 规定,通过`super`调用父类的方法时,方法内部的`this`指向子类。 +ES6 规定,通过`super`调用父类的方法时,方法内部的`this`指向当前的子类实例。 ```javascript class A { @@ -257,9 +257,9 @@ let b = new B(); b.m() // 2 ``` -上面代码中,`super.print()`虽然调用的是`A.prototype.print()`,但是`A.prototype.print()`内部的`this`指向子类`B`,导致输出的是`2`,而不是`1`。也就是说,实际上执行的是`super.print.call(this)`。 +上面代码中,`super.print()`虽然调用的是`A.prototype.print()`,但是`A.prototype.print()`内部的`this`指向子类`B`的实例,导致输出的是`2`,而不是`1`。也就是说,实际上执行的是`super.print.call(this)`。 -由于`this`指向子类,所以如果通过`super`对某个属性赋值,这时`super`就是`this`,赋值的属性会变成子类实例的属性。 +由于`this`指向子类实例,所以如果通过`super`对某个属性赋值,这时`super`就是`this`,赋值的属性会变成子类实例的属性。 ```javascript class A { diff --git a/docs/module-loader.md b/docs/module-loader.md index aa3b23edc..030c40c06 100644 --- a/docs/module-loader.md +++ b/docs/module-loader.md @@ -449,10 +449,9 @@ CommonJS 模块加载 ES6 模块,不能使用`require`命令,而要使用`im // es.mjs let foo = { bar: 'my-default' }; export default foo; -foo = null; // cjs.js -const es_namespace = await import('./es'); +const es_namespace = await import('./es.mjs'); // es_namespace = { // get default() { // ... @@ -462,7 +461,7 @@ console.log(es_namespace.default); // { bar:'my-default' } ``` -上面代码中,`default`接口变成了`es_namespace.default`属性。另外,由于存在缓存机制,`es.mjs`对`foo`的重新赋值没有在模块外部反映出来。 +上面代码中,`default`接口变成了`es_namespace.default`属性。 下面是另一个例子。 @@ -730,7 +729,7 @@ module.exports = function (n) { } ``` -上面代码中,`even.js`加载`odd.js`,而`odd.js`又去加载`even.js`,形成“循环加载”。这时,执行引擎就会输出`even.js`已经执行的部分(不存在任何结果),所以在`odd.js`之中,变量`even`等于`null`,等到后面调用`even(n-1)`就会报错。 +上面代码中,`even.js`加载`odd.js`,而`odd.js`又去加载`even.js`,形成“循环加载”。这时,执行引擎就会输出`even.js`已经执行的部分(不存在任何结果),所以在`odd.js`之中,变量`even`等于`null`,等到后面调用`even(n - 1)`就会报错。 ```bash $ node diff --git a/docs/module.md b/docs/module.md index 0a0f2a95e..c805248d2 100644 --- a/docs/module.md +++ b/docs/module.md @@ -650,7 +650,7 @@ import {db, users} from './index'; ### 简介 -前面介绍过,`import`命令会被 JavaScript 引擎静态分析,先于模块内的其他模块执行(叫做”连接“更合适)。所以,下面的代码会报错。 +前面介绍过,`import`命令会被 JavaScript 引擎静态分析,先于模块内的其他语句执行(`import`命令叫做”连接“ binding 其实更合适)。所以,下面的代码会报错。 ```javascript // 报错 @@ -668,7 +668,7 @@ const path = './' + fileName; const myModual = require(path); ``` -上面的语句就是动态加载,`require`到底加载哪一个模块,只有运行时才知道。`import`语句做不到这一点。 +上面的语句就是动态加载,`require`到底加载哪一个模块,只有运行时才知道。`import`命令做不到这一点。 因此,有一个[提案](https://github.com/tc39/proposal-dynamic-import),建议引入`import()`函数,完成动态加载。 @@ -692,9 +692,34 @@ import(`./section-modules/${someVariable}.js`) }); ``` -`import()`函数可以用在任何地方,不仅仅是模块,非模块的脚本也可以使用。它是运行时执行,也就是说,什么时候运行到这一句,也会加载指定的模块。另外,`import()`函数与所加载的模块没有静态连接关系,这点也是与`import`语句不相同。 +`import()`函数可以用在任何地方,不仅仅是模块,非模块的脚本也可以使用。它是运行时执行,也就是说,什么时候运行到这一句,就会加载指定的模块。另外,`import()`函数与所加载的模块没有静态连接关系,这点也是与`import`语句不相同。 -`import()`类似于 Node 的`require`方法,区别主要是前者是异步加载,后者是同步加载。 +`import()`类似于 Node 的`require`方法,区别主要是前者是异步加载,后者是同步加载。`import()`的浏览器实现,类似于下面的写法。 + +```javascript +function importModule(url) { + return new Promise((resolve, reject) => { + const script = document.createElement("script"); + const tempGlobal = "__tempModuleLoadingVariable" + Math.random().toString(32).substring(2); + script.type = "module"; + script.textContent = `import * as m from "${url}"; window.${tempGlobal} = m;`; + + script.onload = () => { + resolve(window[tempGlobal]); + delete window[tempGlobal]; + script.remove(); + }; + + script.onerror = () => { + reject(new Error("Failed to load module script with URL " + url)); + delete window[tempGlobal]; + script.remove(); + }; + + document.documentElement.appendChild(script); + }); +} +``` ### 适用场合 From 592ab333d8ba6753796106c6bd9018be768b4fb7 Mon Sep 17 00:00:00 2001 From: ruanyf Date: Sun, 25 Mar 2018 20:38:17 +0800 Subject: [PATCH 116/595] docs(object): fix object spread #630 --- docs/number.md | 2 +- docs/object.md | 10 ++++++++-- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/docs/number.md b/docs/number.md index 478db3efd..32bf4b56b 100644 --- a/docs/number.md +++ b/docs/number.md @@ -62,7 +62,7 @@ Number.isNaN('true' / 0) // true Number.isNaN('true' / 'true') // true ``` -注意,如果参数类型不是数值,`Number.isNaN`一律返回`false`。 +如果参数类型不是`NaN`,`Number.isNaN`一律返回`false`。 它们与传统的全局方法`isFinite()`和`isNaN()`的区别在于,传统方法先调用`Number()`将非数值的值转为数值,再进行判断,而这两个新方法只对数值有效,`Number.isFinite()`对于非数值一律返回`false`, `Number.isNaN()`只有对于`NaN`才返回`true`,非`NaN`一律返回`false`。 diff --git a/docs/object.md b/docs/object.md index d6ef05138..b93b53513 100644 --- a/docs/object.md +++ b/docs/object.md @@ -1317,13 +1317,19 @@ o3.a // undefined const o = Object.create({ x: 1, y: 2 }); o.z = 3; -let { x, ...{ y, z } } = o; +let { x, ...newObj } = o; +let { y, z } = newObj; x // 1 y // undefined z // 3 ``` -上面代码中,变量`x`是单纯的解构赋值,所以可以读取对象`o`继承的属性;变量`y`和`z`是扩展运算符的解构赋值,只能读取对象`o`自身的属性,所以变量`z`可以赋值成功,变量`y`取不到值。 +上面代码中,变量`x`是单纯的解构赋值,所以可以读取对象`o`继承的属性;变量`y`和`z`是扩展运算符的解构赋值,只能读取对象`o`自身的属性,所以变量`z`可以赋值成功,变量`y`取不到值。ES6 规定,变量声明语句之中,如果使用解构赋值,扩展运算符后面必须是一个变量名,而不能是一个解构赋值表达式,所以上面代码引入了中间变量`newObj`,如果写成下面这样会报错。 + +```javascript +let { x, ...{ y, z } } = o; +// SyntaxError: ... must be followed by an identifier in declaration contexts +``` 解构赋值的一个用处,是扩展某个函数的参数,引入其他操作。 From 27784cd0cff223ea417ecbbf23e6f212d11c0af9 Mon Sep 17 00:00:00 2001 From: ruanyf Date: Tue, 27 Mar 2018 19:07:42 +0800 Subject: [PATCH 117/595] docs(Proxy): edit Proxy --- docs/proxy.md | 42 +++++++++++++++++++++++++++++++++++++++--- 1 file changed, 39 insertions(+), 3 deletions(-) diff --git a/docs/proxy.md b/docs/proxy.md index bacb8ac01..7daf8afa4 100644 --- a/docs/proxy.md +++ b/docs/proxy.md @@ -145,7 +145,7 @@ fproxy.foo === "Hello, foo" // true ### get() -`get`方法用于拦截某个属性的读取操作,可以接受三个参数,依次为目标对象、属性名和 proxy 实例本身(即`this`关键字指向的那个对象),其中最后一个参数可选。 +`get`方法用于拦截某个属性的读取操作,可以接受三个参数,依次为目标对象、属性名和 proxy 实例本身(严格地说,是操作行为所针对的对象),其中最后一个参数可选。 `get`方法的用法,上文已经有一个例子,下面是另一个拦截读取操作的例子。 @@ -389,9 +389,45 @@ proxy.foo = 'bar'; proxy.foo === proxy // true ``` -上面代码中,`set`方法的第四个参数`receiver`,总是返回`this`关键字所指向的那个对象,即`proxy`实例本身。 +上面代码中,`set`方法的第四个参数`receiver`,指的是操作行为所在的那个对象,一般情况下是`proxy`实例本身,请看下面的例子。 -注意,如果目标对象自身的某个属性,不可写也不可配置,那么`set`不得改变这个属性的值,只能返回同样的值,否则报错。 +```javascript +const handler = { + set: function(obj, prop, value, receiver) { + obj[prop] = receiver; + } +}; +const proxy = new Proxy({}, handler); +const myObj = {}; +Object.setPrototypeOf(myObj, proxy); + +myObj.foo = 'bar'; +myObj.foo === myObj // true +``` + +上面代码中,设置`myObj.foo`属性的值时,`myObj`并没有`foo`属性,因此引擎会到`myObj`的原型链去找`foo`属性。`myObj`的原型对象`proxy`是一个 Proxy 实例,设置它的`foo`属性会触发`set`方法。这时,第四个参数`receiver`就指向原始赋值行为所在的对象`myObj`。 + +注意,如果目标对象自身的某个属性,不可写或不可配置,那么`set`方法将不起作用。 + +```javascript +const obj = {}; +Object.defineProperty(obj, 'foo', { + value: 'bar', + writable: false, +}); + +const handler = { + set: function(obj, prop, value, receiver) { + obj[prop] = 'baz'; + } +}; + +const proxy = new Proxy(obj, handler); +proxy.foo = 'baz'; +proxy.foo // "bar" +``` + +上面代码中,`obj.foo`属性不可写,Proxy 对这个属性的`set`代理将不会生效。 ### apply() From 9b3d8c9afe91e485b50f01ac94f5cbbe5f61ba0f Mon Sep 17 00:00:00 2001 From: Jim Ma Date: Thu, 29 Mar 2018 12:10:52 +0800 Subject: [PATCH 118/595] add SUMMARY.md for gitbook support --- SUMMARY.md | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) create mode 100644 SUMMARY.md diff --git a/SUMMARY.md b/SUMMARY.md new file mode 100644 index 000000000..28dec1cc5 --- /dev/null +++ b/SUMMARY.md @@ -0,0 +1,31 @@ +# Summary + +* [0. 前言](README.md) +* [1. ECMAScript 6简介](docs/intro.md) +* [2. let 和 const 命令](docs/let.md) +* [3. 变量的解构赋值](docs/destructuring.md) +* [4. 字符串的扩展](docs/string.md) +* [5. 正则的扩展](docs/regex.md) +* [6. 数值的扩展](docs/number.md) +* [7. 函数的扩展](docs/function.md) +* [8. 数组的扩展](docs/array.md) +* [9. 对象的扩展](docs/object.md) +* [10. Symbol](docs/symbol.md) +* [11. Set 和 Map 数据结构](docs/set-map.md) +* [12. Proxy](docs/proxy.md) +* [13. Reflect](docs/reflect.md) +* [14. Promise 对象](docs/promise.md) +* [15. Iterator 和 for...of 循环](docs/iterator.md) +* [16. Generator 函数的语法](docs/generator.md) +* [17. Generator 函数的异步应用](docs/generator-async.md) +* [18. async 函数](docs/async.md) +* [19. Class 的基本语法](docs/class.md) +* [20. Class 的继承](docs/class-extends.md) +* [21. Decorator](docs/decorator.md) +* [22. Module 的语法](docs/module.md) +* [23. Module 的加载实现](docs/module-loader.md) +* [24. 编程风格](docs/style.md) +* [25. 读懂规格](docs/spec.md) +* [26. ArrayBuffer](docs/arraybuffer.md) +* [27. 最新提案](docs/proposals.md) +* [28. 参考链接](docs/reference.md) \ No newline at end of file From 02631361e1b664506688f3be5ff7cde590e5c460 Mon Sep 17 00:00:00 2001 From: Jim Ma Date: Thu, 29 Mar 2018 12:22:51 +0800 Subject: [PATCH 119/595] add cover for gitbook --- cover.jpg | 1 + cover_small.jpg | 1 + 2 files changed, 2 insertions(+) create mode 120000 cover.jpg create mode 120000 cover_small.jpg diff --git a/cover.jpg b/cover.jpg new file mode 120000 index 000000000..0fa7635c3 --- /dev/null +++ b/cover.jpg @@ -0,0 +1 @@ +images/cover-3rd.jpg \ No newline at end of file diff --git a/cover_small.jpg b/cover_small.jpg new file mode 120000 index 000000000..61091a8ce --- /dev/null +++ b/cover_small.jpg @@ -0,0 +1 @@ +images/cover_thumbnail_3rd.jpg \ No newline at end of file From f7c8e8800afe8c36d1d0c194cf70f033a2ccb43a Mon Sep 17 00:00:00 2001 From: Jim Ma Date: Thu, 29 Mar 2018 13:09:35 +0800 Subject: [PATCH 120/595] add book.json for gitbook --- book.json | 36 ++++++++++++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) create mode 100644 book.json diff --git a/book.json b/book.json new file mode 100644 index 000000000..be5d775bd --- /dev/null +++ b/book.json @@ -0,0 +1,36 @@ +{ + "author": "阮一峰", + "description": "《ECMAScript 6 入门》是一本开源的 JavaScript 语言教程,全面介绍 ECMAScript 6 新引入的语法特性。", + "extension": null, + "generator": "site", + "isbn": "9787121324758", + "links": { + "sharing": { + "all": null, + "facebook": null, + "google": null, + "twitter": null, + "weibo": null + }, + "sidebar": { + "阮一峰的个人网站": "http://www.ruanyifeng.com/blog/" + } + }, + "output": null, + "pdf": { + "fontSize": 12, + "footerTemplate": null, + "headerTemplate": null, + "margin": { + "bottom": 36, + "left": 62, + "right": 62, + "top": 36 + }, + "pageNumbers": false, + "paperSize": "a4" + }, + "plugins": [], + "title": "ECMAScript 6 入门", + "variables": {} +} \ No newline at end of file From 9e86918b4a87d6840679bdafb92ac67ace6ec591 Mon Sep 17 00:00:00 2001 From: xianshenglu Date: Sat, 31 Mar 2018 11:21:26 +0800 Subject: [PATCH 121/595] =?UTF-8?q?=E5=AE=8C=E5=96=84=E4=B8=8B=E8=A1=A8?= =?UTF-8?q?=E8=BF=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 原表述可能有误导,完善了下 --- docs/async.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/async.md b/docs/async.md index 481315200..4624126e1 100644 --- a/docs/async.md +++ b/docs/async.md @@ -757,7 +757,7 @@ async function f() { // b ``` -上面代码中,`createAsyncIterable()`返回一个异步遍历器,`for...of`循环自动调用这个遍历器的`next`方法,会得到一个 Promise 对象。`await`用来处理这个 Promise 对象,一旦`resolve`,就把得到的值(`x`)传入`for...of`的循环体。 +上面代码中,`createAsyncIterable()`返回一个拥有异步遍历器的对象,`for...of`循环自动调用这个对象的异步遍历器的`next`方法,会得到一个 Promise 对象。`await`用来处理这个 Promise 对象,一旦`resolve`,就把得到的值(`x`)传入`for...of`的循环体。 `for await...of`循环的一个用途,是部署了 asyncIterable 操作的异步接口,可以直接放入这个循环。 From 7fc640d46b949e1809538a0e28dbf966b18bf7c8 Mon Sep 17 00:00:00 2001 From: xianshenglu Date: Sun, 1 Apr 2018 21:23:14 +0800 Subject: [PATCH 122/595] =?UTF-8?q?=E8=A1=A5=E5=85=85=E5=AE=8C=E5=96=84?= =?UTF-8?q?=E8=A1=A8=E8=BF=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/class-extends.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/class-extends.md b/docs/class-extends.md index 9026c6e71..7a6022e1d 100644 --- a/docs/class-extends.md +++ b/docs/class-extends.md @@ -231,7 +231,7 @@ let b = new B(); 上面代码中,属性`x`是定义在`A.prototype`上面的,所以`super.x`可以取到它的值。 -ES6 规定,通过`super`调用父类的方法时,方法内部的`this`指向当前的子类实例。 +ES6 规定,在子类普通方法中通过`super`调用父类的方法(此时调用的是父类普通方法,因为`super`指向父类的原型对象)时,方法内部的`this`指向当前的子类实例;在子类静态方法中通过`super`调用父类的方法(此时调用的是父类静态方法,因为`super`指向父类)时,方法内部的`this`指向当前的子类; ```javascript class A { From 36fc72005778ef1f83357b2b630731d6f03a2066 Mon Sep 17 00:00:00 2001 From: xianshenglu Date: Sun, 1 Apr 2018 21:26:00 +0800 Subject: [PATCH 123/595] =?UTF-8?q?=E5=AE=8C=E5=96=84=E8=A1=A8=E8=BF=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/class-extends.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/class-extends.md b/docs/class-extends.md index 9026c6e71..56991bfbe 100644 --- a/docs/class-extends.md +++ b/docs/class-extends.md @@ -342,7 +342,7 @@ class B extends A { let b = new B(); ``` -上面代码中,`super.valueOf()`表明`super`是一个对象,因此就不会报错。同时,由于`super`使得`this`指向`B`,所以`super.valueOf()`返回的是一个`B`的实例。 +上面代码中,`super.valueOf()`表明`super`是一个对象,因此就不会报错。同时,由于`super`使得`this`指向`B`的实例,所以`super.valueOf()`返回的是一个`B`的实例。 最后,由于对象总是继承其他对象的,所以可以在任意一个对象中,使用`super`关键字。 From 5e2ed331aa2b8599907c6d2f35a254a8be382cd6 Mon Sep 17 00:00:00 2001 From: ruanyf Date: Tue, 3 Apr 2018 18:35:59 +0800 Subject: [PATCH 124/595] docs(class-extends): edit class-extends --- docs/async.md | 2 +- docs/class-extends.md | 30 +++++++++++++++++++++++++++++- docs/regex.md | 2 +- 3 files changed, 31 insertions(+), 3 deletions(-) diff --git a/docs/async.md b/docs/async.md index 4624126e1..64d1ac1f7 100644 --- a/docs/async.md +++ b/docs/async.md @@ -757,7 +757,7 @@ async function f() { // b ``` -上面代码中,`createAsyncIterable()`返回一个拥有异步遍历器的对象,`for...of`循环自动调用这个对象的异步遍历器的`next`方法,会得到一个 Promise 对象。`await`用来处理这个 Promise 对象,一旦`resolve`,就把得到的值(`x`)传入`for...of`的循环体。 +上面代码中,`createAsyncIterable()`返回一个拥有异步遍历器接口的对象,`for...of`循环自动调用这个对象的异步遍历器的`next`方法,会得到一个 Promise 对象。`await`用来处理这个 Promise 对象,一旦`resolve`,就把得到的值(`x`)传入`for...of`的循环体。 `for await...of`循环的一个用途,是部署了 asyncIterable 操作的异步接口,可以直接放入这个循环。 diff --git a/docs/class-extends.md b/docs/class-extends.md index 7a6022e1d..15cb0a636 100644 --- a/docs/class-extends.md +++ b/docs/class-extends.md @@ -231,7 +231,7 @@ let b = new B(); 上面代码中,属性`x`是定义在`A.prototype`上面的,所以`super.x`可以取到它的值。 -ES6 规定,在子类普通方法中通过`super`调用父类的方法(此时调用的是父类普通方法,因为`super`指向父类的原型对象)时,方法内部的`this`指向当前的子类实例;在子类静态方法中通过`super`调用父类的方法(此时调用的是父类静态方法,因为`super`指向父类)时,方法内部的`this`指向当前的子类; +ES6 规定,在子类普通方法中通过`super`调用父类的方法时,方法内部的`this`指向当前的子类实例。 ```javascript class A { @@ -314,6 +314,34 @@ child.myMethod(2); // instance 2 上面代码中,`super`在静态方法之中指向父类,在普通方法之中指向父类的原型对象。 +另外,在子类的静态方法中通过`super`调用父类的方法时,方法内部的`this`指向当前的子类,而不是子类的实例。 + +```javascript +class A { + constructor() { + this.x = 1; + } + static print() { + console.log(this.x); + } +} + +class B extends A { + constructor() { + super(); + this.x = 2; + } + static m() { + super.print(); + } +} + +B.x = 3; +B.m() // 3 +``` + +上面代码中,静态方法`B.m`里面,`super.print`指向父类的静态方法。这个方法里面的`this`指向的是`B`,而不是`B`的实例。 + 注意,使用`super`的时候,必须显式指定是作为函数、还是作为对象使用,否则会报错。 ```javascript diff --git a/docs/regex.md b/docs/regex.md index cf45072d4..4ee07ed01 100644 --- a/docs/regex.md +++ b/docs/regex.md @@ -368,7 +368,7 @@ const RE_DOLLAR_PREFIX = /(?<=\$)foo/g; “后行断言”的实现,需要先匹配`/(?<=y)x/`的`x`,然后再回到左边,匹配`y`的部分。这种“先右后左”的执行顺序,与所有其他正则操作相反,导致了一些不符合预期的行为。 -首先,”后行断言“的组匹配,与正常情况下结果是不一样的。 +首先,后行断言的组匹配,与正常情况下结果是不一样的。 ```javascript /(?<=(\d+)(\d+))$/.exec('1053') // ["", "1", "053"] From 536d2c408c0e70b7a57a3c7da5592ce01eb1cf02 Mon Sep 17 00:00:00 2001 From: Lemon_zhang <897418436@qq.com> Date: Wed, 4 Apr 2018 11:29:25 +0800 Subject: [PATCH 125/595] Update destructuring.md --- docs/destructuring.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/destructuring.md b/docs/destructuring.md index 5f69ec7e3..fcb5e1437 100644 --- a/docs/destructuring.md +++ b/docs/destructuring.md @@ -630,7 +630,7 @@ jQuery.ajax = function (url, { crossDomain = false, global = true, // ... more config -}) { +} = {}) { // ... do stuff }; ``` From 013f7370fd0dc9e2fa363e025a556ad5c33bd95d Mon Sep 17 00:00:00 2001 From: ruanyf Date: Thu, 5 Apr 2018 13:50:18 +0800 Subject: [PATCH 126/595] doc(function): edit arrow function --- docs/function.md | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/docs/function.md b/docs/function.md index 5969a84e0..208ca3970 100644 --- a/docs/function.md +++ b/docs/function.md @@ -594,12 +594,9 @@ ES6 允许使用“箭头”(`=>`)定义函数。 ```javascript var f = v => v; -``` - -上面的箭头函数等同于: -```javascript -var f = function(v) { +// 等同于 +var f = function (v) { return v; }; ``` @@ -634,6 +631,15 @@ let getTempItem = id => { id: id, name: "Temp" }; let getTempItem = id => ({ id: id, name: "Temp" }); ``` +下面是一种特殊情况,虽然可以运行,但会得到错误的结果。 + +```javascript +let foo = () => { a: 1 }; +foo() // undefined +``` + +上面代码中,原始意图是返回一个对象`{ a: 1 }`,但是由于引擎认为大括号是代码块,所以执行了一行语句`a: 1`。这时,`a`可以被解释为语句的标签,因此实际执行的语句是`1;`,然后函数就结束了,没有返回值。 + 如果箭头函数只有一行语句,且不需要返回值,可以采用下面的写法,就不用写大括号了。 ```javascript From 2eccfb5a3508091aba9b1989c7121ce97b7302e1 Mon Sep 17 00:00:00 2001 From: xianshenglu Date: Thu, 5 Apr 2018 18:27:56 +0800 Subject: [PATCH 127/595] =?UTF-8?q?=E5=88=A0=E9=99=A4=E6=97=A0=E6=95=88?= =?UTF-8?q?=E5=AD=97=E7=AC=A6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/spec.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/docs/spec.md b/docs/spec.md index 8a375cfcf..0374a769c 100644 --- a/docs/spec.md +++ b/docs/spec.md @@ -127,15 +127,15 @@ ES6 规格将这个标准流程,使用简写的方式表达。 > Return the result of performing Strict Equality Comparison `x === y`. > 1. If `x` is `null` and `y` is `undefined`, return `true`. > 1. If `x` is `undefined` and `y` is `null`, return `true`. -> 1. If `Type(x)` is Number and `Type(y)` is String,\ +> 1. If `Type(x)` is Number and `Type(y)` is String, > return the result of the comparison `x == ToNumber(y)`. -> 1. If `Type(x)` is String and `Type(y)` is Number,\ +> 1. If `Type(x)` is String and `Type(y)` is Number, > return the result of the comparison `ToNumber(x) == y`. > 1. If `Type(x)` is Boolean, return the result of the comparison `ToNumber(x) == y`. > 1. If `Type(y)` is Boolean, return the result of the comparison `x == ToNumber(y)`. -> 1. If `Type(x)` is either String, Number, or Symbol and `Type(y)` is Object, then\ +> 1. If `Type(x)` is either String, Number, or Symbol and `Type(y)` is Object, then > return the result of the comparison `x == ToPrimitive(y)`. -> 1. If `Type(x)` is Object and `Type(y)` is either String, Number, or Symbol, then\ +> 1. If `Type(x)` is Object and `Type(y)` is either String, Number, or Symbol, then > return the result of the comparison `ToPrimitive(x) == y`. > 1. Return `false`. From 5d16ab071f8cf51ecb9fbda0af9facf24b2151e6 Mon Sep 17 00:00:00 2001 From: ruanyf Date: Mon, 9 Apr 2018 10:47:22 +0800 Subject: [PATCH 128/595] docs(module): edit import() --- docs/module.md | 29 +---------------------------- 1 file changed, 1 insertion(+), 28 deletions(-) diff --git a/docs/module.md b/docs/module.md index c805248d2..4fc5085ef 100644 --- a/docs/module.md +++ b/docs/module.md @@ -692,34 +692,7 @@ import(`./section-modules/${someVariable}.js`) }); ``` -`import()`函数可以用在任何地方,不仅仅是模块,非模块的脚本也可以使用。它是运行时执行,也就是说,什么时候运行到这一句,就会加载指定的模块。另外,`import()`函数与所加载的模块没有静态连接关系,这点也是与`import`语句不相同。 - -`import()`类似于 Node 的`require`方法,区别主要是前者是异步加载,后者是同步加载。`import()`的浏览器实现,类似于下面的写法。 - -```javascript -function importModule(url) { - return new Promise((resolve, reject) => { - const script = document.createElement("script"); - const tempGlobal = "__tempModuleLoadingVariable" + Math.random().toString(32).substring(2); - script.type = "module"; - script.textContent = `import * as m from "${url}"; window.${tempGlobal} = m;`; - - script.onload = () => { - resolve(window[tempGlobal]); - delete window[tempGlobal]; - script.remove(); - }; - - script.onerror = () => { - reject(new Error("Failed to load module script with URL " + url)); - delete window[tempGlobal]; - script.remove(); - }; - - document.documentElement.appendChild(script); - }); -} -``` +`import()`函数可以用在任何地方,不仅仅是模块,非模块的脚本也可以使用。它是运行时执行,也就是说,什么时候运行到这一句,就会加载指定的模块。另外,`import()`函数与所加载的模块没有静态连接关系,这点也是与`import`语句不相同。`import()`类似于 Node 的`require`方法,区别主要是前者是异步加载,后者是同步加载。 ### 适用场合 From 2416e5685bfba66913da708fcf1bfd6f0d60ed2a Mon Sep 17 00:00:00 2001 From: GJ Wang Date: Mon, 9 Apr 2018 22:52:56 +0800 Subject: [PATCH 129/595] Update promise.md shouldn't use cooma. --- docs/promise.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/promise.md b/docs/promise.md index 15b1ca780..83afb2d08 100644 --- a/docs/promise.md +++ b/docs/promise.md @@ -484,7 +484,7 @@ someAsyncThing().then(function() { // carry on [ReferenceError: y is not defined] ``` -上面代码中,第二个`catch`方法用来捕获,前一个`catch`方法抛出的错误。 +上面代码中,第二个`catch`方法用来捕获前一个`catch`方法抛出的错误。 ## Promise.prototype.finally() From caeb8706bd66ab490ca01087081752d6bfc24acf Mon Sep 17 00:00:00 2001 From: ruanyf Date: Sun, 15 Apr 2018 21:31:19 +0800 Subject: [PATCH 130/595] docs(decorator): edit decorator --- docs/decorator.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/decorator.md b/docs/decorator.md index 52dffceb3..3cc385fbf 100644 --- a/docs/decorator.md +++ b/docs/decorator.md @@ -201,7 +201,7 @@ function log(target, name, descriptor) { descriptor.value = function() { console.log(`Calling ${name} with`, arguments); - return oldValue.apply(null, arguments); + return oldValue.apply(this, arguments); }; return descriptor; From 7fab96c932f93e3671f736eafc2b3db3b9088d65 Mon Sep 17 00:00:00 2001 From: ruanyf Date: Tue, 17 Apr 2018 15:53:05 +0800 Subject: [PATCH 131/595] =?UTF-8?q?docs(async):=20=E5=A2=9E=E5=8A=A0=20Str?= =?UTF-8?q?eam=20=E5=BC=82=E6=AD=A5=E9=81=8D=E5=8E=86=E5=99=A8=E7=9A=84?= =?UTF-8?q?=E4=BE=8B=E5=AD=90?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/async.md | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/docs/async.md b/docs/async.md index 64d1ac1f7..0a12272d2 100644 --- a/docs/async.md +++ b/docs/async.md @@ -799,6 +799,37 @@ async function () { // b ``` +Node v10 支持异步遍历器,Stream 就部署了这个接口。下面是读取文件的传统写法与异步遍历器写法的差异。 + +```javascript +// 传统写法 +function main(inputFilePath) { + const readStream = fs.createReadStream( + inputFilePath, + { encoding: 'utf8', highWaterMark: 1024 } + ); + readStream.on('data', (chunk) => { + console.log('>>> '+chunk); + }); + readStream.on('end', () => { + console.log('### DONE ###'); + }); +} + +// 异步遍历器写法 +async function main(inputFilePath) { + const readStream = fs.createReadStream( + inputFilePath, + { encoding: 'utf8', highWaterMark: 1024 } + ); + + for await (const chunk of readStream) { + console.log('>>> '+chunk); + } + console.log('### DONE ###'); +} +``` + ### 异步 Generator 函数 就像 Generator 函数返回一个同步遍历器对象一样,异步 Generator 函数的作用,是返回一个异步遍历器对象。 From b48a35bebbd7d6ad429e7dea351a0ca3da0d0111 Mon Sep 17 00:00:00 2001 From: ruanyf Date: Wed, 18 Apr 2018 15:34:42 +0800 Subject: [PATCH 132/595] docs(proxy): edit proxy.ownkeys --- docs/proxy.md | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/docs/proxy.md b/docs/proxy.md index 7daf8afa4..2f602b848 100644 --- a/docs/proxy.md +++ b/docs/proxy.md @@ -129,7 +129,7 @@ fproxy.foo === "Hello, foo" // true - **set(target, propKey, value, receiver)**:拦截对象属性的设置,比如`proxy.foo = v`或`proxy['foo'] = v`,返回一个布尔值。 - **has(target, propKey)**:拦截`propKey in proxy`的操作,返回一个布尔值。 - **deleteProperty(target, propKey)**:拦截`delete proxy[propKey]`的操作,返回一个布尔值。 -- **ownKeys(target)**:拦截`Object.getOwnPropertyNames(proxy)`、`Object.getOwnPropertySymbols(proxy)`、`Object.keys(proxy)`,返回一个数组。该方法返回目标对象所有自身的属性的属性名,而`Object.keys()`的返回结果仅包括目标对象自身的可遍历属性。 +- **ownKeys(target)**:拦截`Object.getOwnPropertyNames(proxy)`、`Object.getOwnPropertySymbols(proxy)`、`Object.keys(proxy)`、`for...in`循环,返回一个数组。该方法返回目标对象所有自身的属性的属性名,而`Object.keys()`的返回结果仅包括目标对象自身的可遍历属性。 - **getOwnPropertyDescriptor(target, propKey)**:拦截`Object.getOwnPropertyDescriptor(proxy, propKey)`,返回属性的描述对象。 - **defineProperty(target, propKey, propDesc)**:拦截`Object.defineProperty(proxy, propKey, propDesc)`、`Object.defineProperties(proxy, propDescs)`,返回一个布尔值。 - **preventExtensions(target)**:拦截`Object.preventExtensions(proxy)`,返回一个布尔值。 @@ -756,6 +756,7 @@ Object.isExtensible(p) // 报错 - `Object.getOwnPropertyNames()` - `Object.getOwnPropertySymbols()` - `Object.keys()` +- `for...in`循环 下面是拦截`Object.keys()`的例子。 @@ -850,6 +851,23 @@ Object.getOwnPropertyNames(p) // [ 'a', 'b', 'c' ] ``` +`for...in`循环也受到`ownKeys`方法的拦截。 + +```javascript +const obj = { hello: 'world' }; +const proxy = new Proxy(obj, { + ownKeys: function () { + return ['a', 'b']; + } +}); + +for (let key in proxy) { + console.log(key); // 没有任何输出 +} +``` + +上面代码中,`ownkeys`指定只返回`a`和`b`属性,由于`obj`没有这两个属性,因此`for...in`循环不会有任何输出。 + `ownKeys`方法返回的数组成员,只能是字符串或 Symbol 值。如果有其他类型的值,或者返回的根本不是数组,就会报错。 ```javascript From da319a110feb23eeb2c008d8fbde180149946430 Mon Sep 17 00:00:00 2001 From: KinFeng Date: Tue, 24 Apr 2018 15:53:28 +0800 Subject: [PATCH 133/595] =?UTF-8?q?fs=E7=9A=84=E6=8E=A5=E5=8F=A3=E5=BA=94?= =?UTF-8?q?=E8=AF=A5=E6=98=AFreadFile=EF=BC=8C'f'=E6=94=B9=E6=88=90?= =?UTF-8?q?=E5=A4=A7=E5=86=99'F'?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/module-loader.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/module-loader.md b/docs/module-loader.md index 030c40c06..a5ee61500 100644 --- a/docs/module-loader.md +++ b/docs/module-loader.md @@ -426,10 +426,10 @@ setTimeout(_ => module.exports = null); ```javascript // 不正确 -import { readfile } from 'fs'; +import { readFile } from 'fs'; ``` -上面的写法不正确,因为`fs`是 CommonJS 格式,只有在运行时才能确定`readfile`接口,而`import`命令要求编译时就确定这个接口。解决方法就是改为整体输入。 +上面的写法不正确,因为`fs`是 CommonJS 格式,只有在运行时才能确定`readFile`接口,而`import`命令要求编译时就确定这个接口。解决方法就是改为整体输入。 ```javascript // 正确的写法一 From 3226888ad87e3447407ce7941c2ea528fc6e5f61 Mon Sep 17 00:00:00 2001 From: ruanyf Date: Wed, 25 Apr 2018 17:39:57 +0800 Subject: [PATCH 134/595] docs(acknowledgement): edit acknowledgement --- docs/acknowledgment.md | 5 +++++ docs/generator.md | 2 +- 2 files changed, 6 insertions(+), 1 deletion(-) create mode 100644 docs/acknowledgment.md diff --git a/docs/acknowledgment.md b/docs/acknowledgment.md new file mode 100644 index 000000000..818faab9f --- /dev/null +++ b/docs/acknowledgment.md @@ -0,0 +1,5 @@ +# 鸣谢 + +## Generator + +网友 vision57 提出,`next()`、`throw()`、`return()`这三个方法本质上是同一件事,可以放在一起理解。它们的作用都是让 Generator 函数恢复执行,并且使用不同的语句替换`yield`表达式。 diff --git a/docs/generator.md b/docs/generator.md index 56f14e643..fde9a9816 100644 --- a/docs/generator.md +++ b/docs/generator.md @@ -708,7 +708,7 @@ g.next() // { value: 7, done: true } ## next()、throw()、return() 的共同点 -网友 vision57 提出,`next()`、`throw()`、`return()`这三个方法本质上是同一件事,可以放在一起理解。它们的作用都是让 Generator 函数恢复执行,并且使用不同的语句替换`yield`表达式。 +`next()`、`throw()`、`return()`这三个方法本质上是同一件事,可以放在一起理解。它们的作用都是让 Generator 函数恢复执行,并且使用不同的语句替换`yield`表达式。 `next()`是将`yield`表达式替换成一个值。 From 6fc5573b229e756001dfa8aa21e532bd03e7fdf8 Mon Sep 17 00:00:00 2001 From: wuzhiyu5 Date: Sat, 28 Apr 2018 13:57:38 +0800 Subject: [PATCH 135/595] =?UTF-8?q?=E4=BF=AE=E5=A4=8Des6=E6=96=87=E6=A1=A3?= =?UTF-8?q?=E7=AB=A0=E8=8A=82=E9=93=BE=E6=8E=A5=E5=92=8C=E4=BC=AA=E4=BB=A3?= =?UTF-8?q?=E7=A0=81=E6=A0=BC=E5=BC=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/spec.md | 70 ++++++++++++++++++++++++++-------------------------- 1 file changed, 35 insertions(+), 35 deletions(-) diff --git a/docs/spec.md b/docs/spec.md index 0374a769c..3d0cea915 100644 --- a/docs/spec.md +++ b/docs/spec.md @@ -111,7 +111,7 @@ ES6 规格将这个标准流程,使用简写的方式表达。 0 == null ``` -如果你不确定答案,或者想知道语言内部怎么处理,就可以去查看规格,[7.2.12 小节](http://www.ecma-international.org/ecma-262/6.0/#sec-7.2.12)是对相等运算符(`==`)的描述。 +如果你不确定答案,或者想知道语言内部怎么处理,就可以去查看规格,[7.2.12 小节](http://www.ecma-international.org/ecma-262/6.0/#sec-abstract-equality-comparison)是对相等运算符(`==`)的描述。 规格对每一种语法行为的描述,都分成两部分:先是总体的行为描述,然后是实现的算法细节。相等运算符的总体描述,只有一句话。 @@ -123,19 +123,19 @@ ES6 规格将这个标准流程,使用简写的方式表达。 > 1. ReturnIfAbrupt(x). > 1. ReturnIfAbrupt(y). -> 1. If `Type(x)` is the same as `Type(y)`, then\ -> Return the result of performing Strict Equality Comparison `x === y`. +> 1. If `Type(x)` is the same as `Type(y)`, then +> 1. Return the result of performing Strict Equality Comparison `x === y`. > 1. If `x` is `null` and `y` is `undefined`, return `true`. > 1. If `x` is `undefined` and `y` is `null`, return `true`. -> 1. If `Type(x)` is Number and `Type(y)` is String, +> 1. If `Type(x)` is Number and `Type(y)` is String,
        > return the result of the comparison `x == ToNumber(y)`. -> 1. If `Type(x)` is String and `Type(y)` is Number, +> 1. If `Type(x)` is String and `Type(y)` is Number,
        > return the result of the comparison `ToNumber(x) == y`. > 1. If `Type(x)` is Boolean, return the result of the comparison `ToNumber(x) == y`. > 1. If `Type(y)` is Boolean, return the result of the comparison `x == ToNumber(y)`. -> 1. If `Type(x)` is either String, Number, or Symbol and `Type(y)` is Object, then +> 1. If `Type(x)` is either String, Number, or Symbol and `Type(y)` is Object, then
        > return the result of the comparison `x == ToPrimitive(y)`. -> 1. If `Type(x)` is Object and `Type(y)` is either String, Number, or Symbol, then +> 1. If `Type(x)` is Object and `Type(y)` is either String, Number, or Symbol, then
        > return the result of the comparison `ToPrimitive(x) == y`. > 1. Return `false`. @@ -154,7 +154,7 @@ ES6 规格将这个标准流程,使用简写的方式表达。 > 1. 如果`Type(x)`是对象,`Type(y)`是字符串或数值或`Symbol`值,返回`ToPrimitive(x) == y`的结果。 > 1. 返回`false`。 -由于`0`的类型是数值,`null`的类型是 Null(这是规格[4.3.13 小节](http://www.ecma-international.org/ecma-262/6.0/#sec-4.3.13)的规定,是内部 Type 运算的结果,跟`typeof`运算符无关)。因此上面的前 11 步都得不到结果,要到第 12 步才能得到`false`。 +由于`0`的类型是数值,`null`的类型是 Null(这是规格[4.3.13 小节](http://www.ecma-international.org/ecma-262/6.0/#sec-terms-and-definitions-null-type)的规定,是内部 Type 运算的结果,跟`typeof`运算符无关)。因此上面的前 11 步都得不到结果,要到第 12 步才能得到`false`。 ```javascript 0 == null // false @@ -199,7 +199,7 @@ a2.map(n => 1) // [, , ,] 为什么`a1`与`a2`成员的行为不一致?数组的成员是`undefined`或空位,到底有什么不同? -规格的[12.2.5 小节《数组的初始化》](http://www.ecma-international.org/ecma-262/6.0/#sec-12.2.5)给出了答案。 +规格的[12.2.5 小节《数组的初始化》](http://www.ecma-international.org/ecma-262/6.0/#sec-array-initializer)给出了答案。 > “Array elements may be elided at the beginning, middle or end of the element list. Whenever a comma in the element list is not preceded by an AssignmentExpression (i.e., a comma at the beginning or after another comma), the missing array element contributes to the length of the Array and increases the index of subsequent elements. Elided array elements are not defined. If an element is elided at the end of an array, that element does not contribute to the length of the Array.” @@ -215,7 +215,7 @@ a2.map(n => 1) // [, , ,] ## 数组的 map 方法 -规格的[22.1.3.15 小节](http://www.ecma-international.org/ecma-262/6.0/#sec-22.1.3.15)定义了数组的`map`方法。该小节先是总体描述`map`方法的行为,里面没有提到数组空位。 +规格的[22.1.3.15 小节](http://www.ecma-international.org/ecma-262/6.0/#sec-array.prototype.map)定义了数组的`map`方法。该小节先是总体描述`map`方法的行为,里面没有提到数组空位。 后面的算法描述是这样的。 @@ -228,18 +228,18 @@ a2.map(n => 1) // [, , ,] > 1. Let `A` be `ArraySpeciesCreate(O, len)`. > 1. `ReturnIfAbrupt(A)`. > 1. Let `k` be 0. -> 1. Repeat, while `k` < `len`\ -> a. Let `Pk` be `ToString(k)`.\ -> b. Let `kPresent` be `HasProperty(O, Pk)`.\ -> c. `ReturnIfAbrupt(kPresent)`.\ -> d. If `kPresent` is `true`, then\ -> d-1. Let `kValue` be `Get(O, Pk)`.\ -> d-2. `ReturnIfAbrupt(kValue)`.\ -> d-3. Let `mappedValue` be `Call(callbackfn, T, «kValue, k, O»)`.\ -> d-4. `ReturnIfAbrupt(mappedValue)`.\ -> d-5. Let `status` be `CreateDataPropertyOrThrow (A, Pk, mappedValue)`.\ -> d-6. `ReturnIfAbrupt(status)`.\ -> e. Increase `k` by 1. +> 1. Repeat, while `k` < `len` +> 1. Let `Pk` be `ToString(k)`. +> 1. Let `kPresent` be `HasProperty(O, Pk)`. +> 1. `ReturnIfAbrupt(kPresent)`. +> 1. If `kPresent` is `true`, then +> 1. Let `kValue` be `Get(O, Pk)`. +> 1. `ReturnIfAbrupt(kValue)`. +> 1. Let `mappedValue` be `Call(callbackfn, T, «kValue, k, O»)`. +> 1. `ReturnIfAbrupt(mappedValue)`. +> 1. Let `status` be `CreateDataPropertyOrThrow (A, Pk, mappedValue)`. +> 1. `ReturnIfAbrupt(status)`. +> 1. Increase `k` by 1. > 1. Return `A`. 翻译如下。 @@ -253,21 +253,21 @@ a2.map(n => 1) // [, , ,] > 1. 生成一个新的数组`A`,跟当前数组的`length`属性保持一致 > 1. 如果报错就返回 > 1. 设定`k`等于 0 -> 1. 只要`k`小于当前数组的`length`属性,就重复下面步骤\ -> a. 设定`Pk`等于`ToString(k)`,即将`K`转为字符串\ -> b. 设定`kPresent`等于`HasProperty(O, Pk)`,即求当前数组有没有指定属性\ -> c. 如果报错就返回\ -> d. 如果`kPresent`等于`true`,则进行下面步骤\ -> d-1. 设定`kValue`等于`Get(O, Pk)`,取出当前数组的指定属性\ -> d-2. 如果报错就返回\ -> d-3. 设定`mappedValue`等于`Call(callbackfn, T, «kValue, k, O»)`,即执行回调函数\ -> d-4. 如果报错就返回\ -> d-5. 设定`status`等于`CreateDataPropertyOrThrow (A, Pk, mappedValue)`,即将回调函数的值放入`A`数组的指定位置\ -> d-6. 如果报错就返回\ -> e. `k`增加 1 +> 1. 只要`k`小于当前数组的`length`属性,就重复下面步骤 +> 1. 设定`Pk`等于`ToString(k)`,即将`K`转为字符串 +> 1. 设定`kPresent`等于`HasProperty(O, Pk)`,即求当前数组有没有指定属性 +> 1. 如果报错就返回 +> 1. 如果`kPresent`等于`true`,则进行下面步骤 +> 1. 设定`kValue`等于`Get(O, Pk)`,取出当前数组的指定属性 +> 1. 如果报错就返回 +> 1. 设定`mappedValue`等于`Call(callbackfn, T, «kValue, k, O»)`,即执行回调函数 +> 1. 如果报错就返回 +> 1. 设定`status`等于`CreateDataPropertyOrThrow (A, Pk, mappedValue)`,即将回调函数的值放入`A`数组的指定位置 +> 1. 如果报错就返回 +> 1. `k`增加 1 > 1. 返回`A` -仔细查看上面的算法,可以发现,当处理一个全是空位的数组时,前面步骤都没有问题。进入第 10 步的 b 时,`kpresent`会报错,因为空位对应的属性名,对于数组来说是不存在的,因此就会返回,不会进行后面的步骤。 +仔细查看上面的算法,可以发现,当处理一个全是空位的数组时,前面步骤都没有问题。进入第 10 步的 ii 时,`kPresent`会报错,因为空位对应的属性名,对于数组来说是不存在的,因此就会返回,不会进行后面的步骤。 ```javascript const arr = [, , ,]; From 86a012fafd22f09dfe07eaf28d8c8e37cb11ee17 Mon Sep 17 00:00:00 2001 From: wuzhiyu5 Date: Sat, 28 Apr 2018 15:14:33 +0800 Subject: [PATCH 136/595] =?UTF-8?q?=E8=B0=83=E6=95=B4=E6=8F=8F=E8=BF=B0?= =?UTF-8?q?=E4=BB=A5=E7=AC=A6=E5=90=88=E4=BC=AA=E4=BB=A3=E7=A0=81=E4=B8=AD?= =?UTF-8?q?=E5=B1=95=E7=A4=BA=E5=86=85=E5=AE=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/spec.md | 56 ++++++++++++++++++++++++++-------------------------- 1 file changed, 28 insertions(+), 28 deletions(-) diff --git a/docs/spec.md b/docs/spec.md index 3d0cea915..a7005870d 100644 --- a/docs/spec.md +++ b/docs/spec.md @@ -124,18 +124,18 @@ ES6 规格将这个标准流程,使用简写的方式表达。 > 1. ReturnIfAbrupt(x). > 1. ReturnIfAbrupt(y). > 1. If `Type(x)` is the same as `Type(y)`, then -> 1. Return the result of performing Strict Equality Comparison `x === y`. +> 1. Return the result of performing Strict Equality Comparison `x === y`. > 1. If `x` is `null` and `y` is `undefined`, return `true`. > 1. If `x` is `undefined` and `y` is `null`, return `true`. -> 1. If `Type(x)` is Number and `Type(y)` is String,
        +> 1. If `Type(x)` is Number and `Type(y)` is String, > return the result of the comparison `x == ToNumber(y)`. -> 1. If `Type(x)` is String and `Type(y)` is Number,
        +> 1. If `Type(x)` is String and `Type(y)` is Number, > return the result of the comparison `ToNumber(x) == y`. > 1. If `Type(x)` is Boolean, return the result of the comparison `ToNumber(x) == y`. > 1. If `Type(y)` is Boolean, return the result of the comparison `x == ToNumber(y)`. -> 1. If `Type(x)` is either String, Number, or Symbol and `Type(y)` is Object, then
        +> 1. If `Type(x)` is either String, Number, or Symbol and `Type(y)` is Object, then > return the result of the comparison `x == ToPrimitive(y)`. -> 1. If `Type(x)` is Object and `Type(y)` is either String, Number, or Symbol, then
        +> 1. If `Type(x)` is Object and `Type(y)` is either String, Number, or Symbol, then > return the result of the comparison `ToPrimitive(x) == y`. > 1. Return `false`. @@ -229,17 +229,17 @@ a2.map(n => 1) // [, , ,] > 1. `ReturnIfAbrupt(A)`. > 1. Let `k` be 0. > 1. Repeat, while `k` < `len` -> 1. Let `Pk` be `ToString(k)`. -> 1. Let `kPresent` be `HasProperty(O, Pk)`. -> 1. `ReturnIfAbrupt(kPresent)`. -> 1. If `kPresent` is `true`, then -> 1. Let `kValue` be `Get(O, Pk)`. -> 1. `ReturnIfAbrupt(kValue)`. -> 1. Let `mappedValue` be `Call(callbackfn, T, «kValue, k, O»)`. -> 1. `ReturnIfAbrupt(mappedValue)`. -> 1. Let `status` be `CreateDataPropertyOrThrow (A, Pk, mappedValue)`. -> 1. `ReturnIfAbrupt(status)`. -> 1. Increase `k` by 1. +> 1. Let `Pk` be `ToString(k)`. +> 1. Let `kPresent` be `HasProperty(O, Pk)`. +> 1. `ReturnIfAbrupt(kPresent)`. +> 1. If `kPresent` is `true`, then +> 1. Let `kValue` be `Get(O, Pk)`. +> 1. `ReturnIfAbrupt(kValue)`. +> 1. Let `mappedValue` be `Call(callbackfn, T, «kValue, k, O»)`. +> 1. `ReturnIfAbrupt(mappedValue)`. +> 1. Let `status` be `CreateDataPropertyOrThrow (A, Pk, mappedValue)`. +> 1. `ReturnIfAbrupt(status)`. +> 1. Increase `k` by 1. > 1. Return `A`. 翻译如下。 @@ -254,20 +254,20 @@ a2.map(n => 1) // [, , ,] > 1. 如果报错就返回 > 1. 设定`k`等于 0 > 1. 只要`k`小于当前数组的`length`属性,就重复下面步骤 -> 1. 设定`Pk`等于`ToString(k)`,即将`K`转为字符串 -> 1. 设定`kPresent`等于`HasProperty(O, Pk)`,即求当前数组有没有指定属性 -> 1. 如果报错就返回 -> 1. 如果`kPresent`等于`true`,则进行下面步骤 -> 1. 设定`kValue`等于`Get(O, Pk)`,取出当前数组的指定属性 -> 1. 如果报错就返回 -> 1. 设定`mappedValue`等于`Call(callbackfn, T, «kValue, k, O»)`,即执行回调函数 -> 1. 如果报错就返回 -> 1. 设定`status`等于`CreateDataPropertyOrThrow (A, Pk, mappedValue)`,即将回调函数的值放入`A`数组的指定位置 -> 1. 如果报错就返回 -> 1. `k`增加 1 +> 1. 设定`Pk`等于`ToString(k)`,即将`K`转为字符串 +> 1. 设定`kPresent`等于`HasProperty(O, Pk)`,即求当前数组有没有指定属性 +> 1. 如果报错就返回 +> 1. 如果`kPresent`等于`true`,则进行下面步骤 +> 1. 设定`kValue`等于`Get(O, Pk)`,取出当前数组的指定属性 +> 1. 如果报错就返回 +> 1. 设定`mappedValue`等于`Call(callbackfn, T, «kValue, k, O»)`,即执行回调函数 +> 1. 如果报错就返回 +> 1. 设定`status`等于`CreateDataPropertyOrThrow (A, Pk, mappedValue)`,即将回调函数的值放入`A`数组的指定位置 +> 1. 如果报错就返回 +> 1. `k`增加 1 > 1. 返回`A` -仔细查看上面的算法,可以发现,当处理一个全是空位的数组时,前面步骤都没有问题。进入第 10 步的 ii 时,`kPresent`会报错,因为空位对应的属性名,对于数组来说是不存在的,因此就会返回,不会进行后面的步骤。 +仔细查看上面的算法,可以发现,当处理一个全是空位的数组时,前面步骤都没有问题。进入第 10 步中第 2 步时,`kPresent`会报错,因为空位对应的属性名,对于数组来说是不存在的,因此就会返回,不会进行后面的步骤。 ```javascript const arr = [, , ,]; From 165d5e0c19774c50c7778d5374d2a722da1a228c Mon Sep 17 00:00:00 2001 From: ruanyf Date: Thu, 3 May 2018 16:02:42 +0800 Subject: [PATCH 137/595] docs(class): edit class private property --- docs/class.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/class.md b/docs/class.md index f3171c5a6..5f0aa2872 100644 --- a/docs/class.md +++ b/docs/class.md @@ -460,7 +460,7 @@ class Point { } ``` -上面代码中,`#x`就表示私有属性`x`,在`Point`类之外是读取不到这个属性的。还可以看到,私有属性与实例的属性是可以同名的(比如,`#x`与`get x()`)。 +上面代码中,`#x`就是私有属性,在`Point`类之外是读取不到这个属性的。由于井号`#`是属性名的一部分,使用时必须带有`#`一起使用,所以`#x`和`x`是两个不同的属性。 私有属性可以指定初始值,在构造函数执行时进行初始化。 @@ -475,7 +475,7 @@ class Point { 之所以要引入一个新的前缀`#`表示私有属性,而没有采用`private`关键字,是因为 JavaScript 是一门动态语言,使用独立的符号似乎是唯一的可靠方法,能够准确地区分一种属性是否为私有属性。另外,Ruby 语言使用`@`表示私有属性,ES6 没有用这个符号而使用`#`,是因为`@`已经被留给了 Decorator。 -该提案只规定了私有属性的写法。但是,很自然地,它也可以用来写私有方法。 +这种写法不仅可以写私有属性,还可以用来写私有方法。 ```javascript class Foo { From 4533f407a79a65a85df41bc79a3e2b32587da69f Mon Sep 17 00:00:00 2001 From: wuzhiyu5 Date: Sat, 28 Apr 2018 13:57:38 +0800 Subject: [PATCH 138/595] =?UTF-8?q?=E4=BF=AE=E5=A4=8Des6=E6=96=87=E6=A1=A3?= =?UTF-8?q?=E7=AB=A0=E8=8A=82=E9=93=BE=E6=8E=A5=E5=92=8C=E4=BC=AA=E4=BB=A3?= =?UTF-8?q?=E7=A0=81=E6=A0=BC=E5=BC=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/spec.md | 70 ++++++++++++++++++++++++++-------------------------- 1 file changed, 35 insertions(+), 35 deletions(-) diff --git a/docs/spec.md b/docs/spec.md index 0374a769c..3d0cea915 100644 --- a/docs/spec.md +++ b/docs/spec.md @@ -111,7 +111,7 @@ ES6 规格将这个标准流程,使用简写的方式表达。 0 == null ``` -如果你不确定答案,或者想知道语言内部怎么处理,就可以去查看规格,[7.2.12 小节](http://www.ecma-international.org/ecma-262/6.0/#sec-7.2.12)是对相等运算符(`==`)的描述。 +如果你不确定答案,或者想知道语言内部怎么处理,就可以去查看规格,[7.2.12 小节](http://www.ecma-international.org/ecma-262/6.0/#sec-abstract-equality-comparison)是对相等运算符(`==`)的描述。 规格对每一种语法行为的描述,都分成两部分:先是总体的行为描述,然后是实现的算法细节。相等运算符的总体描述,只有一句话。 @@ -123,19 +123,19 @@ ES6 规格将这个标准流程,使用简写的方式表达。 > 1. ReturnIfAbrupt(x). > 1. ReturnIfAbrupt(y). -> 1. If `Type(x)` is the same as `Type(y)`, then\ -> Return the result of performing Strict Equality Comparison `x === y`. +> 1. If `Type(x)` is the same as `Type(y)`, then +> 1. Return the result of performing Strict Equality Comparison `x === y`. > 1. If `x` is `null` and `y` is `undefined`, return `true`. > 1. If `x` is `undefined` and `y` is `null`, return `true`. -> 1. If `Type(x)` is Number and `Type(y)` is String, +> 1. If `Type(x)` is Number and `Type(y)` is String,
        > return the result of the comparison `x == ToNumber(y)`. -> 1. If `Type(x)` is String and `Type(y)` is Number, +> 1. If `Type(x)` is String and `Type(y)` is Number,
        > return the result of the comparison `ToNumber(x) == y`. > 1. If `Type(x)` is Boolean, return the result of the comparison `ToNumber(x) == y`. > 1. If `Type(y)` is Boolean, return the result of the comparison `x == ToNumber(y)`. -> 1. If `Type(x)` is either String, Number, or Symbol and `Type(y)` is Object, then +> 1. If `Type(x)` is either String, Number, or Symbol and `Type(y)` is Object, then
        > return the result of the comparison `x == ToPrimitive(y)`. -> 1. If `Type(x)` is Object and `Type(y)` is either String, Number, or Symbol, then +> 1. If `Type(x)` is Object and `Type(y)` is either String, Number, or Symbol, then
        > return the result of the comparison `ToPrimitive(x) == y`. > 1. Return `false`. @@ -154,7 +154,7 @@ ES6 规格将这个标准流程,使用简写的方式表达。 > 1. 如果`Type(x)`是对象,`Type(y)`是字符串或数值或`Symbol`值,返回`ToPrimitive(x) == y`的结果。 > 1. 返回`false`。 -由于`0`的类型是数值,`null`的类型是 Null(这是规格[4.3.13 小节](http://www.ecma-international.org/ecma-262/6.0/#sec-4.3.13)的规定,是内部 Type 运算的结果,跟`typeof`运算符无关)。因此上面的前 11 步都得不到结果,要到第 12 步才能得到`false`。 +由于`0`的类型是数值,`null`的类型是 Null(这是规格[4.3.13 小节](http://www.ecma-international.org/ecma-262/6.0/#sec-terms-and-definitions-null-type)的规定,是内部 Type 运算的结果,跟`typeof`运算符无关)。因此上面的前 11 步都得不到结果,要到第 12 步才能得到`false`。 ```javascript 0 == null // false @@ -199,7 +199,7 @@ a2.map(n => 1) // [, , ,] 为什么`a1`与`a2`成员的行为不一致?数组的成员是`undefined`或空位,到底有什么不同? -规格的[12.2.5 小节《数组的初始化》](http://www.ecma-international.org/ecma-262/6.0/#sec-12.2.5)给出了答案。 +规格的[12.2.5 小节《数组的初始化》](http://www.ecma-international.org/ecma-262/6.0/#sec-array-initializer)给出了答案。 > “Array elements may be elided at the beginning, middle or end of the element list. Whenever a comma in the element list is not preceded by an AssignmentExpression (i.e., a comma at the beginning or after another comma), the missing array element contributes to the length of the Array and increases the index of subsequent elements. Elided array elements are not defined. If an element is elided at the end of an array, that element does not contribute to the length of the Array.” @@ -215,7 +215,7 @@ a2.map(n => 1) // [, , ,] ## 数组的 map 方法 -规格的[22.1.3.15 小节](http://www.ecma-international.org/ecma-262/6.0/#sec-22.1.3.15)定义了数组的`map`方法。该小节先是总体描述`map`方法的行为,里面没有提到数组空位。 +规格的[22.1.3.15 小节](http://www.ecma-international.org/ecma-262/6.0/#sec-array.prototype.map)定义了数组的`map`方法。该小节先是总体描述`map`方法的行为,里面没有提到数组空位。 后面的算法描述是这样的。 @@ -228,18 +228,18 @@ a2.map(n => 1) // [, , ,] > 1. Let `A` be `ArraySpeciesCreate(O, len)`. > 1. `ReturnIfAbrupt(A)`. > 1. Let `k` be 0. -> 1. Repeat, while `k` < `len`\ -> a. Let `Pk` be `ToString(k)`.\ -> b. Let `kPresent` be `HasProperty(O, Pk)`.\ -> c. `ReturnIfAbrupt(kPresent)`.\ -> d. If `kPresent` is `true`, then\ -> d-1. Let `kValue` be `Get(O, Pk)`.\ -> d-2. `ReturnIfAbrupt(kValue)`.\ -> d-3. Let `mappedValue` be `Call(callbackfn, T, «kValue, k, O»)`.\ -> d-4. `ReturnIfAbrupt(mappedValue)`.\ -> d-5. Let `status` be `CreateDataPropertyOrThrow (A, Pk, mappedValue)`.\ -> d-6. `ReturnIfAbrupt(status)`.\ -> e. Increase `k` by 1. +> 1. Repeat, while `k` < `len` +> 1. Let `Pk` be `ToString(k)`. +> 1. Let `kPresent` be `HasProperty(O, Pk)`. +> 1. `ReturnIfAbrupt(kPresent)`. +> 1. If `kPresent` is `true`, then +> 1. Let `kValue` be `Get(O, Pk)`. +> 1. `ReturnIfAbrupt(kValue)`. +> 1. Let `mappedValue` be `Call(callbackfn, T, «kValue, k, O»)`. +> 1. `ReturnIfAbrupt(mappedValue)`. +> 1. Let `status` be `CreateDataPropertyOrThrow (A, Pk, mappedValue)`. +> 1. `ReturnIfAbrupt(status)`. +> 1. Increase `k` by 1. > 1. Return `A`. 翻译如下。 @@ -253,21 +253,21 @@ a2.map(n => 1) // [, , ,] > 1. 生成一个新的数组`A`,跟当前数组的`length`属性保持一致 > 1. 如果报错就返回 > 1. 设定`k`等于 0 -> 1. 只要`k`小于当前数组的`length`属性,就重复下面步骤\ -> a. 设定`Pk`等于`ToString(k)`,即将`K`转为字符串\ -> b. 设定`kPresent`等于`HasProperty(O, Pk)`,即求当前数组有没有指定属性\ -> c. 如果报错就返回\ -> d. 如果`kPresent`等于`true`,则进行下面步骤\ -> d-1. 设定`kValue`等于`Get(O, Pk)`,取出当前数组的指定属性\ -> d-2. 如果报错就返回\ -> d-3. 设定`mappedValue`等于`Call(callbackfn, T, «kValue, k, O»)`,即执行回调函数\ -> d-4. 如果报错就返回\ -> d-5. 设定`status`等于`CreateDataPropertyOrThrow (A, Pk, mappedValue)`,即将回调函数的值放入`A`数组的指定位置\ -> d-6. 如果报错就返回\ -> e. `k`增加 1 +> 1. 只要`k`小于当前数组的`length`属性,就重复下面步骤 +> 1. 设定`Pk`等于`ToString(k)`,即将`K`转为字符串 +> 1. 设定`kPresent`等于`HasProperty(O, Pk)`,即求当前数组有没有指定属性 +> 1. 如果报错就返回 +> 1. 如果`kPresent`等于`true`,则进行下面步骤 +> 1. 设定`kValue`等于`Get(O, Pk)`,取出当前数组的指定属性 +> 1. 如果报错就返回 +> 1. 设定`mappedValue`等于`Call(callbackfn, T, «kValue, k, O»)`,即执行回调函数 +> 1. 如果报错就返回 +> 1. 设定`status`等于`CreateDataPropertyOrThrow (A, Pk, mappedValue)`,即将回调函数的值放入`A`数组的指定位置 +> 1. 如果报错就返回 +> 1. `k`增加 1 > 1. 返回`A` -仔细查看上面的算法,可以发现,当处理一个全是空位的数组时,前面步骤都没有问题。进入第 10 步的 b 时,`kpresent`会报错,因为空位对应的属性名,对于数组来说是不存在的,因此就会返回,不会进行后面的步骤。 +仔细查看上面的算法,可以发现,当处理一个全是空位的数组时,前面步骤都没有问题。进入第 10 步的 ii 时,`kPresent`会报错,因为空位对应的属性名,对于数组来说是不存在的,因此就会返回,不会进行后面的步骤。 ```javascript const arr = [, , ,]; From 31bef7b0bcc958560d8072508aac4a95ba0c37e5 Mon Sep 17 00:00:00 2001 From: wuzhiyu5 Date: Sat, 28 Apr 2018 15:14:33 +0800 Subject: [PATCH 139/595] =?UTF-8?q?=E8=B0=83=E6=95=B4=E6=8F=8F=E8=BF=B0?= =?UTF-8?q?=E4=BB=A5=E7=AC=A6=E5=90=88=E4=BC=AA=E4=BB=A3=E7=A0=81=E4=B8=AD?= =?UTF-8?q?=E5=B1=95=E7=A4=BA=E5=86=85=E5=AE=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/spec.md | 56 ++++++++++++++++++++++++++-------------------------- 1 file changed, 28 insertions(+), 28 deletions(-) diff --git a/docs/spec.md b/docs/spec.md index 3d0cea915..a7005870d 100644 --- a/docs/spec.md +++ b/docs/spec.md @@ -124,18 +124,18 @@ ES6 规格将这个标准流程,使用简写的方式表达。 > 1. ReturnIfAbrupt(x). > 1. ReturnIfAbrupt(y). > 1. If `Type(x)` is the same as `Type(y)`, then -> 1. Return the result of performing Strict Equality Comparison `x === y`. +> 1. Return the result of performing Strict Equality Comparison `x === y`. > 1. If `x` is `null` and `y` is `undefined`, return `true`. > 1. If `x` is `undefined` and `y` is `null`, return `true`. -> 1. If `Type(x)` is Number and `Type(y)` is String,
        +> 1. If `Type(x)` is Number and `Type(y)` is String, > return the result of the comparison `x == ToNumber(y)`. -> 1. If `Type(x)` is String and `Type(y)` is Number,
        +> 1. If `Type(x)` is String and `Type(y)` is Number, > return the result of the comparison `ToNumber(x) == y`. > 1. If `Type(x)` is Boolean, return the result of the comparison `ToNumber(x) == y`. > 1. If `Type(y)` is Boolean, return the result of the comparison `x == ToNumber(y)`. -> 1. If `Type(x)` is either String, Number, or Symbol and `Type(y)` is Object, then
        +> 1. If `Type(x)` is either String, Number, or Symbol and `Type(y)` is Object, then > return the result of the comparison `x == ToPrimitive(y)`. -> 1. If `Type(x)` is Object and `Type(y)` is either String, Number, or Symbol, then
        +> 1. If `Type(x)` is Object and `Type(y)` is either String, Number, or Symbol, then > return the result of the comparison `ToPrimitive(x) == y`. > 1. Return `false`. @@ -229,17 +229,17 @@ a2.map(n => 1) // [, , ,] > 1. `ReturnIfAbrupt(A)`. > 1. Let `k` be 0. > 1. Repeat, while `k` < `len` -> 1. Let `Pk` be `ToString(k)`. -> 1. Let `kPresent` be `HasProperty(O, Pk)`. -> 1. `ReturnIfAbrupt(kPresent)`. -> 1. If `kPresent` is `true`, then -> 1. Let `kValue` be `Get(O, Pk)`. -> 1. `ReturnIfAbrupt(kValue)`. -> 1. Let `mappedValue` be `Call(callbackfn, T, «kValue, k, O»)`. -> 1. `ReturnIfAbrupt(mappedValue)`. -> 1. Let `status` be `CreateDataPropertyOrThrow (A, Pk, mappedValue)`. -> 1. `ReturnIfAbrupt(status)`. -> 1. Increase `k` by 1. +> 1. Let `Pk` be `ToString(k)`. +> 1. Let `kPresent` be `HasProperty(O, Pk)`. +> 1. `ReturnIfAbrupt(kPresent)`. +> 1. If `kPresent` is `true`, then +> 1. Let `kValue` be `Get(O, Pk)`. +> 1. `ReturnIfAbrupt(kValue)`. +> 1. Let `mappedValue` be `Call(callbackfn, T, «kValue, k, O»)`. +> 1. `ReturnIfAbrupt(mappedValue)`. +> 1. Let `status` be `CreateDataPropertyOrThrow (A, Pk, mappedValue)`. +> 1. `ReturnIfAbrupt(status)`. +> 1. Increase `k` by 1. > 1. Return `A`. 翻译如下。 @@ -254,20 +254,20 @@ a2.map(n => 1) // [, , ,] > 1. 如果报错就返回 > 1. 设定`k`等于 0 > 1. 只要`k`小于当前数组的`length`属性,就重复下面步骤 -> 1. 设定`Pk`等于`ToString(k)`,即将`K`转为字符串 -> 1. 设定`kPresent`等于`HasProperty(O, Pk)`,即求当前数组有没有指定属性 -> 1. 如果报错就返回 -> 1. 如果`kPresent`等于`true`,则进行下面步骤 -> 1. 设定`kValue`等于`Get(O, Pk)`,取出当前数组的指定属性 -> 1. 如果报错就返回 -> 1. 设定`mappedValue`等于`Call(callbackfn, T, «kValue, k, O»)`,即执行回调函数 -> 1. 如果报错就返回 -> 1. 设定`status`等于`CreateDataPropertyOrThrow (A, Pk, mappedValue)`,即将回调函数的值放入`A`数组的指定位置 -> 1. 如果报错就返回 -> 1. `k`增加 1 +> 1. 设定`Pk`等于`ToString(k)`,即将`K`转为字符串 +> 1. 设定`kPresent`等于`HasProperty(O, Pk)`,即求当前数组有没有指定属性 +> 1. 如果报错就返回 +> 1. 如果`kPresent`等于`true`,则进行下面步骤 +> 1. 设定`kValue`等于`Get(O, Pk)`,取出当前数组的指定属性 +> 1. 如果报错就返回 +> 1. 设定`mappedValue`等于`Call(callbackfn, T, «kValue, k, O»)`,即执行回调函数 +> 1. 如果报错就返回 +> 1. 设定`status`等于`CreateDataPropertyOrThrow (A, Pk, mappedValue)`,即将回调函数的值放入`A`数组的指定位置 +> 1. 如果报错就返回 +> 1. `k`增加 1 > 1. 返回`A` -仔细查看上面的算法,可以发现,当处理一个全是空位的数组时,前面步骤都没有问题。进入第 10 步的 ii 时,`kPresent`会报错,因为空位对应的属性名,对于数组来说是不存在的,因此就会返回,不会进行后面的步骤。 +仔细查看上面的算法,可以发现,当处理一个全是空位的数组时,前面步骤都没有问题。进入第 10 步中第 2 步时,`kPresent`会报错,因为空位对应的属性名,对于数组来说是不存在的,因此就会返回,不会进行后面的步骤。 ```javascript const arr = [, , ,]; From 25cc21754d68e97f9b0d44bad70c31934448e47b Mon Sep 17 00:00:00 2001 From: ruanyf Date: Thu, 3 May 2018 16:57:30 +0800 Subject: [PATCH 140/595] docs(class-extends): fix super() #657 --- docs/class-extends.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/class-extends.md b/docs/class-extends.md index c96d8f77e..e19c5631b 100644 --- a/docs/class-extends.md +++ b/docs/class-extends.md @@ -29,7 +29,7 @@ class ColorPoint extends Point { 上面代码中,`constructor`方法和`toString`方法之中,都出现了`super`关键字,它在这里表示父类的构造函数,用来新建父类的`this`对象。 -子类必须在`constructor`方法中调用`super`方法,否则新建实例时会报错。这是因为子类没有自己的`this`对象,而是继承父类的`this`对象,然后对其进行加工。如果不调用`super`方法,子类就得不到`this`对象。 +子类必须在`constructor`方法中调用`super`方法,否则新建实例时会报错。这是因为子类自己的`this`对象,必须先通过父类的构造函数完成塑造,得到与父类同样的实例属性和方法,然后再对其进行加工,加上子类自己的实例属性和方法。如果不调用`super`方法,子类就得不到`this`对象。 ```javascript class Point { /* ... */ } From 6733d07c719765cb909edb5ef618cda648bf99b1 Mon Sep 17 00:00:00 2001 From: ruanyf Date: Thu, 3 May 2018 17:21:01 +0800 Subject: [PATCH 141/595] docs(proposal): edit BigInt --- docs/proposals.md | 39 +++++++++++++++++++++++++++++---------- 1 file changed, 29 insertions(+), 10 deletions(-) diff --git a/docs/proposals.md b/docs/proposals.md index 06af7ae34..a502f9796 100644 --- a/docs/proposals.md +++ b/docs/proposals.md @@ -535,6 +535,13 @@ Math.pow(2, 1024) // Infinity 现在有一个[提案](https://github.com/tc39/proposal-bigint),引入了一种新的数据类型 BigInt(大整数),来解决这个问题。BigInt 只用来表示整数,没有位数的限制,任何位数的整数都可以精确表示。 +```javascript +const a = 2172141653n; +const b = 15346349309n; +a * b // 33334444555566667777n +Number(a) * Number(b) // 33334444555566670000 +``` + 为了与 Number 类型区别,BigInt 类型的数据必须使用后缀`n`表示。 ```javascript @@ -553,7 +560,7 @@ BigInt 同样可以使用各种进制表示,都要加上后缀`n`。 `typeof`运算符对于 BigInt 类型的数据返回`bigint`。 ```javascript -typeof 123n // 'BigInt' +typeof 123n // 'bigint' ``` ### BigInt 对象 @@ -625,29 +632,41 @@ Integer 类型不能与 Number 类型进行混合运算。 上面代码报错是因为无论返回的是 BigInt 或 Number,都会导致丢失信息。比如`(2n**53n + 1n) + 0.5`这个表达式,如果返回 BigInt 类型,`0.5`这个小数部分会丢失;如果返回 Number 类型,有效精度只能保持 53 位,导致精度下降。 +同样的原因,如果一个标准库函数的参数预期是 Number 类型,但是得到的是一个 BigInt,就会报错。 + +```javascript +// 错误的写法 +Math.sqrt(4n) // 报错 + +// 正确的写法 +Math.sqrt(Number(4n)) // 2 +``` + +上面代码中,`Math.sqrt`的参数预期是 Number 类型,如果是 BigInt 就会报错,必须先用`Number`方法转一下类型,才能进行计算。 + asm.js 里面,`|0`跟在一个数值的后面会返回一个32位整数。根据不能与 Number 类型混合运算的规则,BigInt 如果与`|0`进行运算会报错。 ```javascript 1n | 0 // 报错 ``` -相等运算符(`==`)会改变数据类型,也是不允许混合使用。 +比较运算符(比如`>`)和相等运算符(`==`)允许 BigInt 与其他类型的值混合计算,因为这样做不会损失精度。 ```javascript -0n == 0 -// 报错 TypeError - -0n == false -// 报错 TypeError +0n < 1 // true +0n < true // true +0n == 0 // true +0n == false // true ``` -精确相等运算符(`===`)不会改变数据类型,因此可以混合使用。 +同理,精确相等运算符(`===`)也可以混合使用。 ```javascript -0n === 0 -// false +0n === 0 // false ``` +上面代码中,由于`0n`与`0`的数据类型不同,所以返回`false`。 + 大整数可以转为其他数据类型。 ```javascript From 58767f55b2e339cb1d095b21083f42e1c965cc7b Mon Sep 17 00:00:00 2001 From: ruanyf Date: Wed, 9 May 2018 11:26:52 +0800 Subject: [PATCH 142/595] docs(async): edit async --- docs/async.md | 2 +- docs/module.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/async.md b/docs/async.md index 0a12272d2..b411cb6c5 100644 --- a/docs/async.md +++ b/docs/async.md @@ -913,7 +913,7 @@ async function* prefixLines(asyncIterable) { } ``` -异步 Generator 函数的返回值是一个异步 Iterator,即每次调用它的`next`方法,会返回一个 Promise 对象,也就是说,跟在`yield`命令后面的,应该是一个 Promise 对象。 +异步 Generator 函数的返回值是一个异步 Iterator,即每次调用它的`next`方法,会返回一个 Promise 对象,也就是说,跟在`yield`命令后面的,应该是一个 Promise 对象。如果像上面那个例子那样,`yield`命令后面是一个字符串,会被自动包装成一个 Promise 对象。 ```javascript function fetchRandom() { diff --git a/docs/module.md b/docs/module.md index 4fc5085ef..f9ddc8ed4 100644 --- a/docs/module.md +++ b/docs/module.md @@ -650,7 +650,7 @@ import {db, users} from './index'; ### 简介 -前面介绍过,`import`命令会被 JavaScript 引擎静态分析,先于模块内的其他语句执行(`import`命令叫做”连接“ binding 其实更合适)。所以,下面的代码会报错。 +前面介绍过,`import`命令会被 JavaScript 引擎静态分析,先于模块内的其他语句执行(`import`命令叫做“连接” binding 其实更合适)。所以,下面的代码会报错。 ```javascript // 报错 From df00e8abba44601954f61dca0d0cf964587d4e22 Mon Sep 17 00:00:00 2001 From: ruanyf Date: Wed, 9 May 2018 13:06:28 +0800 Subject: [PATCH 143/595] docs(set): edit WeakMap --- docs/set-map.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/set-map.md b/docs/set-map.md index 9d930845b..d223fca4b 100644 --- a/docs/set-map.md +++ b/docs/set-map.md @@ -988,7 +988,7 @@ wm.get(key) ### WeakMap 的语法 -WeakMap 与 Map 在 API 上的区别主要是两个,一是没有遍历操作(即没有`key()`、`values()`和`entries()`方法),也没有`size`属性。因为没有办法列出所有键名,某个键名是否存在完全不可预测,跟垃圾回收机制是否运行相关。这一刻可以取到键名,下一刻垃圾回收机制突然运行了,这个键名就没了,为了防止出现不确定性,就统一规定不能取到键名。二是无法清空,即不支持`clear`方法。因此,`WeakMap`只有四个方法可用:`get()`、`set()`、`has()`、`delete()`。 +WeakMap 与 Map 在 API 上的区别主要是两个,一是没有遍历操作(即没有`keys()`、`values()`和`entries()`方法),也没有`size`属性。因为没有办法列出所有键名,某个键名是否存在完全不可预测,跟垃圾回收机制是否运行相关。这一刻可以取到键名,下一刻垃圾回收机制突然运行了,这个键名就没了,为了防止出现不确定性,就统一规定不能取到键名。二是无法清空,即不支持`clear`方法。因此,`WeakMap`只有四个方法可用:`get()`、`set()`、`has()`、`delete()`。 ```javascript const wm = new WeakMap(); From 7d5a56292aefec91b000a691143de6fdd2dfbe27 Mon Sep 17 00:00:00 2001 From: ruanyf Date: Wed, 16 May 2018 14:16:28 +0800 Subject: [PATCH 144/595] docs(proxy): edit receiver --- docs/proxy.md | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) diff --git a/docs/proxy.md b/docs/proxy.md index 2f602b848..c7dfa8c04 100644 --- a/docs/proxy.md +++ b/docs/proxy.md @@ -277,7 +277,7 @@ const el = dom.div({}, document.body.appendChild(el); ``` -下面是一个`get`方法的第三个参数的例子。 +下面是一个`get`方法的第三个参数的例子,它总是指向原始的读操作所在的那个对象,一般情况下就是 Proxy 实例。 ```javascript const proxy = new Proxy({}, { @@ -288,7 +288,20 @@ const proxy = new Proxy({}, { proxy.getReceiver === proxy // true ``` -上面代码中,`get`方法的第三个参数`receiver`,总是为当前的 Proxy 实例。 +上面代码中,`proxy`对象的`getReceiver`属性是由`proxy`对象提供的,所以`receiver`指向`proxy`对象。 + +```javascript +const proxy = new Proxy({}, { + get: function(target, property, receiver) { + return receiver; + } +}); + +const d = Object.create(proxy); +d.a === d // true +``` + +上面代码中,`d`对象本身没有`a`属性,所以读取`d.a`的时候,会去`d`的原型`proxy`对象找。这时,`receiver`就指向`d`,代表原始的读操作所在的那个对象。 如果一个属性不可配置(configurable)和不可写(writable),则该属性不能被代理,通过 Proxy 对象访问该属性会报错。 @@ -389,7 +402,7 @@ proxy.foo = 'bar'; proxy.foo === proxy // true ``` -上面代码中,`set`方法的第四个参数`receiver`,指的是操作行为所在的那个对象,一般情况下是`proxy`实例本身,请看下面的例子。 +上面代码中,`set`方法的第四个参数`receiver`,指的是原始的操作行为所在的那个对象,一般情况下是`proxy`实例本身,请看下面的例子。 ```javascript const handler = { From 9708a2158135c5fa82a0930ed0a2d465463de9c5 Mon Sep 17 00:00:00 2001 From: Simon Ma Date: Wed, 16 May 2018 20:43:29 +0800 Subject: [PATCH 145/595] Update proxy.md MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 1. 纠正错别字 > **构建函数** 为 构造函数 2. 纠正语义 > 有被`for...in`循环所排除。 3. 增加 > `has` 方法的参数说明 --- docs/proxy.md | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/docs/proxy.md b/docs/proxy.md index c7dfa8c04..e486078da 100644 --- a/docs/proxy.md +++ b/docs/proxy.md @@ -503,6 +503,8 @@ Reflect.apply(proxy, null, [9, 10]) // 38 `has`方法用来拦截`HasProperty`操作,即判断对象是否具有某个属性时,这个方法会生效。典型的操作就是`in`运算符。 +`has`方法可以接受两个参数,分别是目标对象、需查询的属性名。 + 下面的例子使用`has`方法隐藏某些属性,不被`in`运算符发现。 ```javascript @@ -579,7 +581,7 @@ for (let b in oproxy2) { // 99 ``` -上面代码中,`has`拦截只对`in`运算符生效,对`for...in`循环不生效,导致不符合要求的属性没有被排除在`for...in`循环之外。 +上面代码中,`has`拦截只对`in`运算符生效,对`for...in`循环不生效,导致不符合要求的属性没有被`for...in`循环所排除。 ### construct() @@ -596,7 +598,7 @@ var handler = { `construct`方法可以接受两个参数。 - `target`: 目标对象 -- `args`:构建函数的参数对象 +- `args`:构造函数的参数对象 下面是一个例子。 From 2d86fc3f9c1302bf1c6926bb8ff41e2d25b8e5c1 Mon Sep 17 00:00:00 2001 From: ruanyf Date: Thu, 17 May 2018 22:39:21 +0800 Subject: [PATCH 146/595] docs(generator): edit throw --- docs/generator.md | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/docs/generator.md b/docs/generator.md index fde9a9816..89127cfbd 100644 --- a/docs/generator.md +++ b/docs/generator.md @@ -534,6 +534,24 @@ g.throw(); 上面代码中,`g.throw`抛出错误以后,没有任何`try...catch`代码块可以捕获这个错误,导致程序报错,中断执行。 +`throw`方法抛出的错误要被内部捕获,前提是必须至少执行过一次`next`方法。 + +```javascript +function* gen() { + try { + yield 1; + } catch (e) { + console.log('内部捕获'); + } +} + +var g = gen(); +g.throw(1); +// Uncaught 1 +``` + +上面代码中,`g.throw(1)`执行时,`next`方法一次都没有执行过。这时,抛出的错误不会被内部捕获,而是直接在外部抛出,导致程序出错。这种行为其实很好理解,因为第一次执行`next`方法,等同于启动执行 Generator 函数的内部代码,否则 Generator 函数还没有开始执行,这时`throw`方法抛错只可能抛出在函数外部。 + `throw`方法被捕获以后,会附带执行下一条`yield`表达式。也就是说,会附带执行一次`next`方法。 ```javascript From 7c7f1ce7d1cec390f6fad2a605b962b08b07dd19 Mon Sep 17 00:00:00 2001 From: ruanyf Date: Sun, 20 May 2018 06:37:23 +0800 Subject: [PATCH 147/595] docs(proxy): edit proxyw --- docs/generator.md | 2 +- docs/proxy.md | 12 +++++------- docs/regex.md | 20 +++++++++++++++++--- 3 files changed, 23 insertions(+), 11 deletions(-) diff --git a/docs/generator.md b/docs/generator.md index 89127cfbd..4e5a0a128 100644 --- a/docs/generator.md +++ b/docs/generator.md @@ -1233,7 +1233,7 @@ var clock = function* () { Generator 函数是 ES6 对协程的实现,但属于不完全实现。Generator 函数被称为“半协程”(semi-coroutine),意思是只有 Generator 函数的调用者,才能将程序的执行权还给 Generator 函数。如果是完全执行的协程,任何函数都可以让暂停的协程继续执行。 -如果将 Generator 函数当作协程,完全可以将多个需要互相协作的任务写成 Generator 函数,它们之间使用`yield`表示式交换控制权。 +如果将 Generator 函数当作协程,完全可以将多个需要互相协作的任务写成 Generator 函数,它们之间使用`yield`表达式交换控制权。 ### Generator 与上下文 diff --git a/docs/proxy.md b/docs/proxy.md index c7dfa8c04..51f02d40a 100644 --- a/docs/proxy.md +++ b/docs/proxy.md @@ -595,10 +595,9 @@ var handler = { `construct`方法可以接受两个参数。 -- `target`: 目标对象 -- `args`:构建函数的参数对象 - -下面是一个例子。 +- `target`:目标对象 +- `args`:构造函数的参数对象 +- `newTarget`:创造实例对象时,`new`命令作用的构造函数(下面例子的`p`) ```javascript var p = new Proxy(function () {}, { @@ -664,11 +663,10 @@ var handler = { }; var target = {}; var proxy = new Proxy(target, handler); -proxy.foo = 'bar' -// TypeError: proxy defineProperty handler returned false for property '"foo"' +proxy.foo = 'bar' // 不会生效 ``` -上面代码中,`defineProperty`方法返回`false`,导致添加新属性会抛出错误。 +上面代码中,`defineProperty`方法返回`false`,导致添加新属性总是无效。 注意,如果目标对象不可扩展(extensible),则`defineProperty`不能增加目标对象上不存在的属性,否则会报错。另外,如果目标对象的某个属性不可写(writable)或不可配置(configurable),则`defineProperty`方法不得改变这两个设置。 diff --git a/docs/regex.md b/docs/regex.md index 4ee07ed01..71390bfba 100644 --- a/docs/regex.md +++ b/docs/regex.md @@ -132,6 +132,20 @@ codePointLength(s) // 2 上面代码中,不加`u`修饰符,就无法识别非规范的`K`字符。 +## RegExp.prototype.unicode 属性 + +正则实例对象新增`unicode`属性,表示是否设置了`u`修饰符。 + +```javascript +const r1 = /hello/; +const r2 = /hello/u; + +r1.unicode // false +r2.unicode // true +``` + +上面代码中,正则表达式是否设置了`u`修饰符,可以从`unicode`属性看出来。 + ## y 修饰符 除了`u`修饰符,ES6 还为正则表达式添加了`y`修饰符,叫做“粘连”(sticky)修饰符。 @@ -264,16 +278,16 @@ tokenize(TOKEN_G, '3x + 4') 上面代码中,`g`修饰符会忽略非法字符,而`y`修饰符不会,这样就很容易发现错误。 -## sticky 属性 +## RegExp.prototype.sticky 属性 -与`y`修饰符相匹配,ES6 的正则对象多了`sticky`属性,表示是否设置了`y`修饰符。 +与`y`修饰符相匹配,ES6 的正则实例对象多了`sticky`属性,表示是否设置了`y`修饰符。 ```javascript var r = /hello\d/y; r.sticky // true ``` -## flags 属性 +## RegExp.prototype.flags 属性 ES6 为正则表达式新增了`flags`属性,会返回正则表达式的修饰符。 From 1ac13240a9ca81ebb5d615a1df77a87ccc9db628 Mon Sep 17 00:00:00 2001 From: 07akioni <07akioni2@gmail.com> Date: Mon, 21 May 2018 14:31:10 +0800 Subject: [PATCH 148/595] =?UTF-8?q?=E4=BF=AE=E6=94=B9=E4=BA=86=E6=A8=A1?= =?UTF-8?q?=E5=9D=97=E5=BE=AA=E7=8E=AF=E5=BA=94=E7=94=A8=E7=AB=A0=E8=8A=82?= =?UTF-8?q?=E4=B8=80=E4=B8=AA=E5=8F=98=E9=87=8F=E7=9A=84=E9=94=99=E8=AF=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit odd 中的 even 应为 undefined 而不是 null --- docs/module-loader.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/module-loader.md b/docs/module-loader.md index a5ee61500..106410fdf 100644 --- a/docs/module-loader.md +++ b/docs/module-loader.md @@ -729,7 +729,7 @@ module.exports = function (n) { } ``` -上面代码中,`even.js`加载`odd.js`,而`odd.js`又去加载`even.js`,形成“循环加载”。这时,执行引擎就会输出`even.js`已经执行的部分(不存在任何结果),所以在`odd.js`之中,变量`even`等于`null`,等到后面调用`even(n - 1)`就会报错。 +上面代码中,`even.js`加载`odd.js`,而`odd.js`又去加载`even.js`,形成“循环加载”。这时,执行引擎就会输出`even.js`已经执行的部分(不存在任何结果),所以在`odd.js`之中,变量`even`等于`undefined`,等到后面调用`even(n - 1)`就会报错。 ```bash $ node From 76b6bce980e830a84038ce0caf54aeafa796697e Mon Sep 17 00:00:00 2001 From: ruanyf Date: Tue, 22 May 2018 12:34:53 +0800 Subject: [PATCH 149/595] docs(generator): edit generator --- docs/class-extends.md | 2 +- docs/generator.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/class-extends.md b/docs/class-extends.md index e19c5631b..a81df72a9 100644 --- a/docs/class-extends.md +++ b/docs/class-extends.md @@ -499,7 +499,7 @@ A.__proto__ === Function.prototype // true A.prototype.__proto__ === undefined // true ``` -这种情况与第二种情况非常像。`A`也是一个普通函数,所以直接继承`Function.prototype`。但是,`A`调用后返回的对象不继承任何方法,所以它的`__proto__`指向`Function.prototype`,即实质上执行了下面的代码。 +这种情况与第二种情况非常像。`A`也是一个普通函数,所以直接继承`Function.prototype`。但是,`A`调用后返回的对象不继承任何方法,所以它的`__proto__`指向`undefined`,即实质上执行了下面的代码。 ```javascript class C extends null { diff --git a/docs/generator.md b/docs/generator.md index 4e5a0a128..e679bf2a7 100644 --- a/docs/generator.md +++ b/docs/generator.md @@ -336,8 +336,8 @@ for (let v of foo()) { function* fibonacci() { let [prev, curr] = [0, 1]; for (;;) { - [prev, curr] = [curr, prev + curr]; yield curr; + [prev, curr] = [curr, prev + curr]; } } From 4f5db47055d1a7380fa38394d0eaf6b222ec7988 Mon Sep 17 00:00:00 2001 From: taxilng <769967440@qq.com> Date: Tue, 22 May 2018 14:30:21 +0800 Subject: [PATCH 150/595] Update object.md MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit __proto__属性:第一个例子,注释,ES5,ES6写反了 --- docs/object.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/object.md b/docs/object.md index b93b53513..1ca5a90cf 100644 --- a/docs/object.md +++ b/docs/object.md @@ -861,13 +861,13 @@ JavaScript 语言的对象继承是通过原型链实现的。ES6 提供了更 `__proto__`属性(前后各两个下划线),用来读取或设置当前对象的`prototype`对象。目前,所有浏览器(包括 IE11)都部署了这个属性。 ```javascript -// es6 的写法 +// es5 的写法 const obj = { method: function() { ... } }; obj.__proto__ = someOtherObj; -// es5 的写法 +// es6 的写法 var obj = Object.create(someOtherObj); obj.method = function() { ... }; ``` From 54eba0dc17a4b31f0b87c5f4bf7e5834bc68e5ec Mon Sep 17 00:00:00 2001 From: taxilng <769967440@qq.com> Date: Thu, 24 May 2018 10:30:28 +0800 Subject: [PATCH 151/595] Update symbol.md MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit demo不能在游览器上直接运行,故修改下; --- docs/symbol.md | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/docs/symbol.md b/docs/symbol.md index 90126a310..f86005be1 100644 --- a/docs/symbol.md +++ b/docs/symbol.md @@ -163,13 +163,15 @@ let obj = { Symbol 类型还可以用于定义一组常量,保证这组常量的值都是不相等的。 ```javascript +const log = {}; + log.levels = { DEBUG: Symbol('debug'), INFO: Symbol('info'), WARN: Symbol('warn') }; -log(log.levels.DEBUG, 'debug message'); -log(log.levels.INFO, 'info message'); +console.log(log.levels.DEBUG, 'debug message'); +console.log(log.levels.INFO, 'info message'); ``` 下面是另外一个例子。 From dfd5c0ac0e0039dabf91999393675adbdce385c4 Mon Sep 17 00:00:00 2001 From: shenzhim Date: Thu, 31 May 2018 19:30:36 +0800 Subject: [PATCH 152/595] =?UTF-8?q?@std/esm=20=E5=B7=B2=E7=BB=8Fdeprecated?= =?UTF-8?q?=EF=BC=8C=E6=94=B9=E4=B8=BAesm?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/async.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/async.md b/docs/async.md index b411cb6c5..02c174e3d 100644 --- a/docs/async.md +++ b/docs/async.md @@ -460,7 +460,7 @@ async function dbFuc(db) { } ``` -目前,[`@std/esm`](https://www.npmjs.com/package/@std/esm)模块加载器支持顶层`await`,即`await`命令可以不放在 async 函数里面,直接使用。 +目前,[`esm`](https://www.npmjs.com/package/esm)模块加载器支持顶层`await`,即`await`命令可以不放在 async 函数里面,直接使用。 ```javascript // async 函数的写法 @@ -476,7 +476,7 @@ const res = await fetch('google.com'); console.log(await res.text()); ``` -上面代码中,第二种写法的脚本必须使用`@std/esm`加载器,才会生效。 +上面代码中,第二种写法的脚本必须使用`esm`加载器,才会生效。 ## async 函数的实现原理 From 42e06a84d9ef75f7547ff4584802494c55d3c82a Mon Sep 17 00:00:00 2001 From: ruanyf Date: Mon, 4 Jun 2018 11:12:30 +0800 Subject: [PATCH 153/595] docs(reference): edit reference --- docs/proxy.md | 4 ++-- docs/reference.md | 1 + 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/docs/proxy.md b/docs/proxy.md index d7bfbfe3e..6120bbae3 100644 --- a/docs/proxy.md +++ b/docs/proxy.md @@ -303,7 +303,7 @@ d.a === d // true 上面代码中,`d`对象本身没有`a`属性,所以读取`d.a`的时候,会去`d`的原型`proxy`对象找。这时,`receiver`就指向`d`,代表原始的读操作所在的那个对象。 -如果一个属性不可配置(configurable)和不可写(writable),则该属性不能被代理,通过 Proxy 对象访问该属性会报错。 +如果一个属性不可配置(configurable)且不可写(writable),则 Proxy 不能修改该属性,否则通过 Proxy 对象访问该属性会报错。 ```javascript const target = Object.defineProperties({}, { @@ -420,7 +420,7 @@ myObj.foo === myObj // true 上面代码中,设置`myObj.foo`属性的值时,`myObj`并没有`foo`属性,因此引擎会到`myObj`的原型链去找`foo`属性。`myObj`的原型对象`proxy`是一个 Proxy 实例,设置它的`foo`属性会触发`set`方法。这时,第四个参数`receiver`就指向原始赋值行为所在的对象`myObj`。 -注意,如果目标对象自身的某个属性,不可写或不可配置,那么`set`方法将不起作用。 +注意,如果目标对象自身的某个属性,不可写且不可配置,那么`set`方法将不起作用。 ```javascript const obj = {}; diff --git a/docs/reference.md b/docs/reference.md index 795c61ee2..9d2330fe1 100644 --- a/docs/reference.md +++ b/docs/reference.md @@ -140,6 +140,7 @@ ## Promise 对象 - Jake Archibald, [JavaScript Promises: There and back again](http://www.html5rocks.com/en/tutorials/es6/promises/) +- Jake Archibald, [Tasks, microtasks, queues and schedules](https://jakearchibald.com/2015/tasks-microtasks-queues-and-schedules/) - Tilde, [rsvp.js](https://github.com/tildeio/rsvp.js) - Sandeep Panda, [An Overview of JavaScript Promises](http://www.sitepoint.com/overview-javascript-promises/): ES6 Promise 入门介绍 - Dave Atchley, [ES6 Promises](http://www.datchley.name/es6-promises/): Promise 的语法介绍 From 688ca45e6fb0ad3e0d3c5904dde3ccf486fc7a37 Mon Sep 17 00:00:00 2001 From: ruanyf Date: Tue, 5 Jun 2018 13:33:35 +0800 Subject: [PATCH 154/595] docs(Reflect): fix Reflect.setPrototypeOf #693 --- docs/reflect.md | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/docs/reflect.md b/docs/reflect.md index ff3fd579b..7ee1dbcfd 100644 --- a/docs/reflect.md +++ b/docs/reflect.md @@ -313,16 +313,27 @@ Reflect.getPrototypeOf(1) // 报错 ### Reflect.setPrototypeOf(obj, newProto) -`Reflect.setPrototypeOf`方法用于设置对象的`__proto__`属性,返回第一个参数对象,对应`Object.setPrototypeOf(obj, newProto)`。 +`Reflect.setPrototypeOf`方法用于设置目标对象的原型(prototype),对应`Object.setPrototypeOf(obj, newProto)`方法。它返回一个布尔值,表示是否设置成功。 ```javascript -const myObj = new FancyThing(); +const myObj = {}; // 旧写法 -Object.setPrototypeOf(myObj, OtherThing.prototype); +Object.setPrototypeOf(myObj, Array.prototype); // 新写法 -Reflect.setPrototypeOf(myObj, OtherThing.prototype); +Reflect.setPrototypeOf(myObj, Array.prototype); + +myObj.length // 0 +``` + +如果无法设置目标对象的原型(比如,目标对象禁止扩展),`Reflect.setPrototypeOf`方法返回`false`。 + +```javascript +Reflect.setPrototypeOf({}, null) +// true +Reflect.setPrototypeOf(Object.freeze({}), null) +// false ``` 如果第一个参数不是对象,`Object.setPrototypeOf`会返回第一个参数本身,而`Reflect.setPrototypeOf`会报错。 From cdc7e48d939cab5a0027664b65bcd228ede46ee8 Mon Sep 17 00:00:00 2001 From: ruanyf Date: Tue, 5 Jun 2018 16:48:43 +0800 Subject: [PATCH 155/595] docs(array): edit spread operator #694 --- docs/array.md | 30 ++++++++++++++++++++---------- 1 file changed, 20 insertions(+), 10 deletions(-) diff --git a/docs/array.md b/docs/array.md index 30137c236..7f6fd379f 100644 --- a/docs/array.md +++ b/docs/array.md @@ -163,24 +163,34 @@ const [...a2] = a1; 扩展运算符提供了数组合并的新写法。 ```javascript -// ES5 -[1, 2].concat(more) -// ES6 -[1, 2, ...more] - -var arr1 = ['a', 'b']; -var arr2 = ['c']; -var arr3 = ['d', 'e']; +const arr1 = ['a', 'b']; +const arr2 = ['c']; +const arr3 = ['d', 'e']; -// ES5的合并数组 +// ES5 的合并数组 arr1.concat(arr2, arr3); // [ 'a', 'b', 'c', 'd', 'e' ] -// ES6的合并数组 +// ES6 的合并数组 [...arr1, ...arr2, ...arr3] // [ 'a', 'b', 'c', 'd', 'e' ] ``` +不过,这两种方法都是浅拷贝,使用的时候需要注意。 + +```javascript +const a1 = [{ foo: 1 }]; +const a2 = [{ bar: 2 }]; + +const a3 = a1.concat(a2); +const a4 = [...a1, ...a2]; + +a3[0] === a1[0] // true +a4[0] === a1[0] // true +``` + +上面代码中,`a3`和`a4`是用两种不同方法合并而成的新数组,但是它们的成员都是对原数组成员的引用,这就是浅拷贝。如果修改了原数组的成员,会同步反映到新数组。 + **(3)与解构赋值结合** 扩展运算符可以与解构赋值结合起来,用于生成数组。 From 6583714f3bb1ef5e921c3064bbccc97d2f04f2f8 Mon Sep 17 00:00:00 2001 From: snow212-cn Date: Thu, 7 Jun 2018 22:34:19 +0800 Subject: [PATCH 156/595] Update object.md MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 这两段不知所云的英文插进代码段要干什么 --- docs/object.md | 4 ---- 1 file changed, 4 deletions(-) diff --git a/docs/object.md b/docs/object.md index 1ca5a90cf..a1c2bdc2c 100644 --- a/docs/object.md +++ b/docs/object.md @@ -1426,8 +1426,6 @@ let newVersion = { ```javascript let aWithDefaults = { x: 1, y: 2, ...a }; // 等同于 - even if property keys don’t clash, because objects record insertion order: - let aWithDefaults = Object.assign({}, { x: 1, y: 2 }, a); // 等同于 let aWithDefaults = Object.assign({ x: 1, y: 2 }, a); @@ -1447,8 +1445,6 @@ const obj = { ```javascript {...{}, a: 1} // { a: 1 } - even if property keys don’t clash, because objects record insertion order: - ``` 如果扩展运算符的参数是`null`或`undefined`,这两个值会被忽略,不会报错。 From e4351e2a70cff6ff58337fd313b11875d3eba2cc Mon Sep 17 00:00:00 2001 From: favefan Date: Thu, 21 Jun 2018 09:28:48 +0800 Subject: [PATCH 157/595] word change. --- docs/let.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/let.md b/docs/let.md index 112022907..026b8aefb 100644 --- a/docs/let.md +++ b/docs/let.md @@ -591,7 +591,7 @@ ES5 的顶层对象,本身也是一个问题,因为它在各种实现里面 - 全局环境中,`this`会返回顶层对象。但是,Node 模块和 ES6 模块中,`this`返回的是当前模块。 - 函数里面的`this`,如果函数不是作为对象的方法运行,而是单纯作为函数运行,`this`会指向顶层对象。但是,严格模式下,这时`this`会返回`undefined`。 -- 不管是严格模式,还是普通模式,`new Function('return this')()`,总是会返回全局对象。但是,如果浏览器用了 CSP(Content Security Policy,内容安全政策),那么`eval`、`new Function`这些方法都可能无法使用。 +- 不管是严格模式,还是普通模式,`new Function('return this')()`,总是会返回全局对象。但是,如果浏览器用了 CSP(Content Security Policy,内容安全策略),那么`eval`、`new Function`这些方法都可能无法使用。 综上所述,很难找到一种方法,可以在所有情况下,都取到顶层对象。下面是两种勉强可以使用的方法。 From 19e1974afe27c5b1ab192470618e1d2a989c3078 Mon Sep 17 00:00:00 2001 From: ruanyf Date: Thu, 21 Jun 2018 16:40:25 +0800 Subject: [PATCH 158/595] docs: edit class-extends --- docs/class-extends.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/class-extends.md b/docs/class-extends.md index a81df72a9..3d39842f5 100644 --- a/docs/class-extends.md +++ b/docs/class-extends.md @@ -713,7 +713,7 @@ function mix(...mixins) { class Mix {} for (let mixin of mixins) { - copyProperties(Mix, mixin); // 拷贝实例属性 + copyProperties(Mix.prototype, new mixin()); // 拷贝实例属性 copyProperties(Mix.prototype, mixin.prototype); // 拷贝原型属性 } From 83bad90346c8c2486d8fa844bd7a0463757428bf Mon Sep 17 00:00:00 2001 From: ruanyf Date: Thu, 21 Jun 2018 16:57:09 +0800 Subject: [PATCH 159/595] docs: edit class-extends --- docs/class-extends.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/class-extends.md b/docs/class-extends.md index 3d39842f5..867868d0c 100644 --- a/docs/class-extends.md +++ b/docs/class-extends.md @@ -713,8 +713,8 @@ function mix(...mixins) { class Mix {} for (let mixin of mixins) { - copyProperties(Mix.prototype, new mixin()); // 拷贝实例属性 - copyProperties(Mix.prototype, mixin.prototype); // 拷贝原型属性 + copyProperties(Mix.prototype, mixin); // 拷贝实例属性 + copyProperties(Mix.prototype, Object.getPrototypeOf(mixin)); // 拷贝原型属性 } return Mix; From 3c44084f4b2e318fcbec77b7191b1f2412726c47 Mon Sep 17 00:00:00 2001 From: ruanyf Date: Mon, 25 Jun 2018 12:26:32 +0800 Subject: [PATCH 160/595] =?UTF-8?q?docs(class-extends):=20=E5=88=A0?= =?UTF-8?q?=E9=99=A4=20class=20X=20extends=20null=20=E7=9A=84=E8=AE=A8?= =?UTF-8?q?=E8=AE=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/class-extends.md | 28 +++------------------------- 1 file changed, 3 insertions(+), 25 deletions(-) diff --git a/docs/class-extends.md b/docs/class-extends.md index 867868d0c..098b81bdc 100644 --- a/docs/class-extends.md +++ b/docs/class-extends.md @@ -452,8 +452,6 @@ Object.create(A.prototype); B.prototype.__proto__ = A.prototype; ``` -### extends 的继承目标 - `extends`关键字后面可以跟多种类型的值。 ```javascript @@ -463,9 +461,7 @@ class B extends A { 上面代码的`A`,只要是一个有`prototype`属性的函数,就能被`B`继承。由于函数都有`prototype`属性(除了`Function.prototype`函数),因此`A`可以是任意函数。 -下面,讨论三种特殊情况。 - -第一种特殊情况,子类继承`Object`类。 +下面,讨论两种情况。第一种,子类继承`Object`类。 ```javascript class A extends Object { @@ -477,7 +473,7 @@ A.prototype.__proto__ === Object.prototype // true 这种情况下,`A`其实就是构造函数`Object`的复制,`A`的实例就是`Object`的实例。 -第二种特殊情况,不存在任何继承。 +第二种情况,不存在任何继承。 ```javascript class A { @@ -489,24 +485,6 @@ A.prototype.__proto__ === Object.prototype // true 这种情况下,`A`作为一个基类(即不存在任何继承),就是一个普通函数,所以直接继承`Function.prototype`。但是,`A`调用后返回一个空对象(即`Object`实例),所以`A.prototype.__proto__`指向构造函数(`Object`)的`prototype`属性。 -第三种特殊情况,子类继承`null`。 - -```javascript -class A extends null { -} - -A.__proto__ === Function.prototype // true -A.prototype.__proto__ === undefined // true -``` - -这种情况与第二种情况非常像。`A`也是一个普通函数,所以直接继承`Function.prototype`。但是,`A`调用后返回的对象不继承任何方法,所以它的`__proto__`指向`undefined`,即实质上执行了下面的代码。 - -```javascript -class C extends null { - constructor() { return Object.create(null); } -} -``` - ### 实例的 \_\_proto\_\_ 属性 子类实例的`__proto__`属性的`__proto__`属性,指向父类实例的`__proto__`属性。也就是说,子类的原型的原型,是父类的原型。 @@ -714,7 +692,7 @@ function mix(...mixins) { for (let mixin of mixins) { copyProperties(Mix.prototype, mixin); // 拷贝实例属性 - copyProperties(Mix.prototype, Object.getPrototypeOf(mixin)); // 拷贝原型属性 + copyProperties(Mix.prototype, Reflect.getPrototypeOf(mixin)); // 拷贝原型属性 } return Mix; From e85f4dd15a17713d076524f8f131f7f0ddb04450 Mon Sep 17 00:00:00 2001 From: ruanyf Date: Mon, 2 Jul 2018 14:16:27 +0800 Subject: [PATCH 161/595] docs(iterator): edit return() #708 --- docs/iterator.md | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/docs/iterator.md b/docs/iterator.md index a05753f63..a8091062f 100644 --- a/docs/iterator.md +++ b/docs/iterator.md @@ -475,7 +475,7 @@ for (let x of obj) { 遍历器对象除了具有`next`方法,还可以具有`return`方法和`throw`方法。如果你自己写遍历器对象生成函数,那么`next`方法是必须部署的,`return`方法和`throw`方法是否部署是可选的。 -`return`方法的使用场合是,如果`for...of`循环提前退出(通常是因为出错,或者有`break`语句或`continue`语句),就会调用`return`方法。如果一个对象在完成遍历前,需要清理或释放资源,就可以部署`return`方法。 +`return`方法的使用场合是,如果`for...of`循环提前退出(通常是因为出错,或者有`break`语句),就会调用`return`方法。如果一个对象在完成遍历前,需要清理或释放资源,就可以部署`return`方法。 ```javascript function readLinesSync(file) { @@ -495,7 +495,7 @@ function readLinesSync(file) { } ``` -上面代码中,函数`readLinesSync`接受一个文件对象作为参数,返回一个遍历器对象,其中除了`next`方法,还部署了`return`方法。下面的三种情况,都会触发执行`return`方法。 +上面代码中,函数`readLinesSync`接受一个文件对象作为参数,返回一个遍历器对象,其中除了`next`方法,还部署了`return`方法。下面的两种情况,都会触发执行`return`方法。 ```javascript // 情况一 @@ -505,12 +505,6 @@ for (let line of readLinesSync(fileName)) { } // 情况二 -for (let line of readLinesSync(fileName)) { - console.log(line); - continue; -} - -// 情况三 for (let line of readLinesSync(fileName)) { console.log(line); throw new Error(); From efafe678d1fe16c9a405a7b328941d33a460ac53 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Shaoyao=C2=B7=E7=90=9A?= Date: Wed, 4 Jul 2018 08:11:26 +0200 Subject: [PATCH 162/595] Update module.md MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 修正描述 --- docs/module.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/module.md b/docs/module.md index f9ddc8ed4..f27c0f87f 100644 --- a/docs/module.md +++ b/docs/module.md @@ -453,7 +453,7 @@ export default 42; export 42; ``` -上面代码中,后一句报错是因为没有指定对外的接口,而前一句指定外对接口为`default`。 +上面代码中,后一句报错是因为没有指定对外的接口,而前一句指定对外接口为`default`。 有了`export default`命令,输入模块时就非常直观了,以输入 lodash 模块为例。 @@ -643,7 +643,7 @@ export {users} from './users'; ```javascript // script.js -import {db, users} from './index'; +import {db, users} from './constants/index'; ``` ## import() From d4c8df426ab9942a745584998754fe0ae114e44d Mon Sep 17 00:00:00 2001 From: ruanyf Date: Fri, 6 Jul 2018 10:32:43 +0800 Subject: [PATCH 163/595] docs(class-extends): fix super() #711 --- docs/class-extends.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/class-extends.md b/docs/class-extends.md index 098b81bdc..617dfaf05 100644 --- a/docs/class-extends.md +++ b/docs/class-extends.md @@ -44,7 +44,7 @@ let cp = new ColorPoint(); // ReferenceError 上面代码中,`ColorPoint`继承了父类`Point`,但是它的构造函数没有调用`super`方法,导致新建实例时报错。 -ES5 的继承,实质是先创造子类的实例对象`this`,然后再将父类的方法添加到`this`上面(`Parent.apply(this)`)。ES6 的继承机制完全不同,实质是先创造父类的实例对象`this`(所以必须先调用`super`方法),然后再用子类的构造函数修改`this`。 +ES5 的继承,实质是先创造子类的实例对象`this`,然后再将父类的方法添加到`this`上面(`Parent.apply(this)`)。ES6 的继承机制完全不同,实质是先将父类实例对象的属性和方法,加到`this`上面(所以必须先调用`super`方法),然后再用子类的构造函数修改`this`。 如果子类没有定义`constructor`方法,这个方法会被默认添加,代码如下。也就是说,不管有没有显式定义,任何一个子类都有`constructor`方法。 @@ -60,7 +60,7 @@ class ColorPoint extends Point { } ``` -另一个需要注意的地方是,在子类的构造函数中,只有调用`super`之后,才可以使用`this`关键字,否则会报错。这是因为子类实例的构建,是基于对父类实例加工,只有`super`方法才能返回父类实例。 +另一个需要注意的地方是,在子类的构造函数中,只有调用`super`之后,才可以使用`this`关键字,否则会报错。这是因为子类实例的构建,基于父类实例,只有`super`方法才能调用父类实例。 ```javascript class Point { From a46664f54abb9742fe22cb31285b1cac8c7929a9 Mon Sep 17 00:00:00 2001 From: ruanyf Date: Fri, 6 Jul 2018 10:36:01 +0800 Subject: [PATCH 164/595] docs(iterator): fix return() --- docs/iterator.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/iterator.md b/docs/iterator.md index a8091062f..a39352c4a 100644 --- a/docs/iterator.md +++ b/docs/iterator.md @@ -511,7 +511,7 @@ for (let line of readLinesSync(fileName)) { } ``` -上面代码中,情况一输出文件的第一行以后,就会执行`return`方法,关闭这个文件;情况二输出所有行以后,执行`return`方法,关闭该文件;情况三会在执行`return`方法关闭文件之后,再抛出错误。 +上面代码中,情况一输出文件的第一行以后,就会执行`return`方法,关闭这个文件;情况二会在执行`return`方法关闭文件之后,再抛出错误。 注意,`return`方法必须返回一个对象,这是 Generator 规格决定的。 From 94024df0f113114bf7ad9e5293fa2d5c8da19f77 Mon Sep 17 00:00:00 2001 From: hanty <841609790@qq.com> Date: Wed, 11 Jul 2018 14:46:51 +0800 Subject: [PATCH 165/595] Update async.md --- docs/async.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/docs/async.md b/docs/async.md index 02c174e3d..b3931fa28 100644 --- a/docs/async.md +++ b/docs/async.md @@ -722,9 +722,10 @@ async function f() { 注意,异步遍历器的`next`方法是可以连续调用的,不必等到上一步产生的 Promise 对象`resolve`以后再调用。这种情况下,`next`方法会累积起来,自动按照每一步的顺序运行下去。下面是一个例子,把所有的`next`方法放在`Promise.all`方法里面。 ```javascript -const asyncGenObj = createAsyncIterable(['a', 'b']); +const asyncIterable = createAsyncIterable(['a', 'b']); +const asyncIterator = asyncIterable[Symbol.asyncIterator](); const [{value: v1}, {value: v2}] = await Promise.all([ - asyncGenObj.next(), asyncGenObj.next() + asyncIterator.next(), asyncIterator.next() ]); console.log(v1, v2); // a b From 0fe548fd2b7c8a6a073104a2c52faa75fff41f86 Mon Sep 17 00:00:00 2001 From: ruanyf Date: Tue, 17 Jul 2018 12:25:16 +0800 Subject: [PATCH 166/595] docs(let): edit const #720 --- docs/let.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/let.md b/docs/let.md index 026b8aefb..a8b4ddec6 100644 --- a/docs/let.md +++ b/docs/let.md @@ -494,7 +494,7 @@ const age = 30; ### 本质 -`const`实际上保证的,并不是变量的值不得改动,而是变量指向的那个内存地址不得改动。对于简单类型的数据(数值、字符串、布尔值),值就保存在变量指向的那个内存地址,因此等同于常量。但对于复合类型的数据(主要是对象和数组),变量指向的内存地址,保存的只是一个指针,`const`只能保证这个指针是固定的,至于它指向的数据结构是不是可变的,就完全不能控制了。因此,将一个对象声明为常量必须非常小心。 +`const`实际上保证的,并不是变量的值不得改动,而是变量指向的那个内存地址不得改动。对于简单类型的数据(数值、字符串、布尔值),值就保存在变量指向的那个内存地址,因此等同于常量。但对于复合类型的数据(主要是对象和数组),变量指向的内存地址,保存的只是一个指向实际数据的指针,`const`只能保证这个指针是固定的(即总是指向另一个固定的地址),至于它指向的数据结构是不是可变的,就完全不能控制了。因此,将一个对象声明为常量必须非常小心。 ```javascript const foo = {}; From 420a9f76f8651a511c73e0a09cba6cef80afba93 Mon Sep 17 00:00:00 2001 From: ruanyf Date: Tue, 17 Jul 2018 21:42:52 +0800 Subject: [PATCH 167/595] docs(let): fix const #720 --- docs/let.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/let.md b/docs/let.md index 112022907..a2dae6fa6 100644 --- a/docs/let.md +++ b/docs/let.md @@ -494,7 +494,7 @@ const age = 30; ### 本质 -`const`实际上保证的,并不是变量的值不得改动,而是变量指向的那个内存地址不得改动。对于简单类型的数据(数值、字符串、布尔值),值就保存在变量指向的那个内存地址,因此等同于常量。但对于复合类型的数据(主要是对象和数组),变量指向的内存地址,保存的只是一个指针,`const`只能保证这个指针是固定的,至于它指向的数据结构是不是可变的,就完全不能控制了。因此,将一个对象声明为常量必须非常小心。 +`const`实际上保证的,并不是变量的值不得改动,而是变量指向的那个内存地址所保存的数据不得改动。对于简单类型的数据(数值、字符串、布尔值),值就保存在变量指向的那个内存地址,因此等同于常量。但对于复合类型的数据(主要是对象和数组),变量指向的内存地址,保存的只是一个指向实际数据的指针,`const`只能保证这个指针是固定的(即总是指向另一个固定的地址),至于它指向的数据结构是不是可变的,就完全不能控制了。因此,将一个对象声明为常量必须非常小心。 ```javascript const foo = {}; From 3034d8e4ed0e29a6bcd3c1e5699536a351037c7f Mon Sep 17 00:00:00 2001 From: ruanyf Date: Tue, 24 Jul 2018 11:06:49 +0800 Subject: [PATCH 168/595] refact: cancel inline JS codes --- config.js | 1 + index.html | 3 --- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/config.js b/config.js index 2ea5eb093..f0c3ad930 100644 --- a/config.js +++ b/config.js @@ -24,3 +24,4 @@ function addConfig(obj, conf) { }); } +ditto.run(); diff --git a/index.html b/index.html index 66a636001..98936328a 100644 --- a/index.html +++ b/index.html @@ -28,9 +28,6 @@
        -