2017-09-01
第十一章 Promise与异步编程
使用 Promise.reject()
若你传递一个 Promise 给
Promise.resolve()
或Promise.reject()
方法,该 Promise 会不作修改原样返回。
修改为:
若你传递一个 Promise 给
Promise.resolve()
,该 Promise 会不作修改原样返回。
并删除以下译注:
译注: 经过测试,在几大浏览器中都存在与上一句话不符的情况。
- 若传入的 Promise 为进行态,则
Promise.resolve()
调用会将该 Promise 原样返回。此后,若决议原 Promise ,在then()
中可以接收到原例中的参数42
;而若拒绝原 Promise ,则在catch()
中可以接收到参数42
。 但Promise.reject()
调用则会对原先的 Promise 重新进行包装,对其使用catch()
可以捕捉到错误,处理函数中的value
参数不会是数值42
,而是原先处于进行态的 Promise 。- 若传入的 Promise 为完成态,则
Promise.resolve()
调用会将该 Promise 原样返回,在then()
中可以接收到原例中的参数42
。 但Promise.reject()
调用则会对原先的 Promise 重新进行包装,对其使用catch()
可以捕捉到错误,处理函数中的value
参数不会是数值42
,而是原先处于完成态的 Promise 。- 若传入的 Promise 为拒绝态,则
Promise.reject()
调用会将该 Promise 原样返回,在catch()
中可以接收到参数42
。 但Promise.resolve()
调用则会对原先的 Promise 重新进行包装,对其使用then()
可以进行完成处理,处理函数中的value
参数不是42
,而是原先处于拒绝态的 Promise 。也就是说此时的情况与上一种情况相反。总结:对进行态或完成态的 Promise 使用
Promise.resolve()
没问题,会返回原 Promise ;对拒绝态的 Promise 使用Promise.reject()
也没问题。而除此之外的情况全都会在原 Promise 上包装出一个新的 Promise 。
在目前的浏览器中测试结果为: Promise.resolve()
会将传入的 Promise 类型的参数直接返回,无论该 Promise 处于何种状态。而 Promise.resolve()
则会对传入的任意对象参数进行 Promise 包装,返回一个新的 Promise 对象,无论传入参数是否为 Promise 类型,也不论参数是否为已处于拒绝态的 Promise 。这样的行为也符合了 ES6 的规范,其中 Promise.resolve()
会根据对象参数的 constructor
属性来判断它是否为一个 Promise 对象。
上述附注已不符合实际情况,因此予以删除。原文唯一问题是对于 Promise.reject()
方法的使用。
异步任务运行
删除以下译注:
译注:此处的
Promise.resolve()
在被传入任意 Promise 时只会直接将其传递回来表述不够准确,详情参见前面的译注。
理由同上。
2017-08-20
总体修改说明
对全部译文重新进行了检查,修改了 700 多处。不过绝大部分仅仅是修改了表述方式,包括调整语序、修改关联词、减少括号的使用等。此外:
原文中对其他章节的引用,许多地方提到的章节序号都有错,都进行了修正,不一一列出。
根据正式中文版图书,对少数名词的翻译进行了调整,包括:
- 将“需计算属性名”更改为“可计算属性名”。
- 将“Object.assign() 方法”一节中提到的“供应者”更改为“源对象”。
- “一级公民”与“一级函数”改为“一等公民”与“一等函数”,两种译法都有,但后者貌似更常见一些。
- Promise 部分,将
pending
、unsettled
与settled
分别改译为“进行中”、“未处理”、“已处理”。
除此之外的错误都列在了后面,其中有些是原作的问题,有些则是翻译错误。
第三章 函数
剩余参数
函数的
length
属性用于指示具名参数的数量,而剩余参数对其毫无影响。此例中pick()
函数的length
属性值是 1 ,因为只有object
参数被用于计算该值。
这段话后添加一段译注:
译注:这种说法并不严谨。若函数使用了默认参数,则
length
属性不包含使用默认值的参数,并且它只能指示出第一个默认参数之前的具名参数数量。例如对于function example(first, second = 'woo', third) {}
函数声明来说,length
的值是1
而非2
,尽管这里有两个无默认值的具名参数。
第四章 扩展的对象功能
使用 super 引用的简单原型访问
此例使用了一个函数作为具名方法
修改为:
此例使用了一个匿名函数作为属性
原文的 named 用于修饰属性,而不是函数,该函数实际上是个匿名函数。
第五章 解构:更方便的数据访问
参数解构的默认值
在这一小节最后部分添加附注:
上面的参数解构只有一个缺点,也就是当传入参数值为
null
时会引发程序异常。只有null
与undefined
是无法被解构的,而传入undefined
会触发默认参数的使用条件,从而避免了异常;但传入null
就不会有这么幸运了,它既不会触发默认参数,也不能被解构,从而导致异常。
第六章 符号与符号属性
符号值的转换
但若你想直接将符号转换为字符串,则会引发错误
修改为:
但若你想将符号与字符串直接拼接,则会引发错误
使用知名符号暴露内部方法
它只是改变了规范所描述的对象特征
改为:
它只是改变了规范描述对象的方式
第七章 Set与Map
使用 Weak Map
“非空对象”修改为“非 null 的对象”,后面“Weak Map 的初始化”小节也要作此修改。
“空对象”在 js 中一般指的是 {}
,这与 null
并不一样。
对象的私有数据
此例用 IIFE 包裹了
Person
的定义,其中含有两个私有属性
修改为:
此例用 IIFE 包裹了
Person
的定义,其中含有两个私有变量
第八章 迭代器与生成器
字符串的迭代器
因此它不能被用于正确访问双字节的字符
修改为:
因此它不能被用于正确访问双码元的字符
属于原作的错误。前面曾经介绍过,编码单元本身就是两个字节(16位)的, JS 从前就可以处理一个码元(双字节)的字符,例如各种中文字符。旧版 JS 所不能处理的是双编码单元(一共32位,即四个字节)的字符,例如后面提到的那个日文字符。
同理——
由于双字节字符被当作两个分离的码元来对待
修改为:
由于四字节字符被当作两个分离的码元来对待
传递参数给迭代器
关于“首次调用 next() 时传递参数是无效”这一问题,将原文的翻译与译者个人的理解对调了一下位置,后者放到附注部分。
第十章 增强的数组功能
在可迭代对象上使用
如果一个对象既是类数组对象,又是可迭代对象,那么迭代器就会使用
Array.from()
方法来决定需要转换的值。
修改为:
如果一个对象既是类数组对象,又是可迭代对象,那么
Array.from()
方法就会使用迭代器来决定需要转换的值。
类型化数组
而不像名称暗示的那样,能处理所有类型
修改为:
正如名称所示,不是所有类型
第十二章 代理与反射接口
使用 get 陷阱函数进行对象外形验证
receiver
并没有使用trapTarget
,而是用了in
修改为:
使用
receiver
而非trapTarget
去配合in
将代理作为类的原型
shape
的原型是Shape.prototype
,它并不是一个代理。
原作笔误,应修改为:
shape
的原型是Square.prototype
,它并不是一个代理。
第十三章 用模块封装代码
浏览器模块说明符方案
你无须确保
ww2.example.com
已经正确配置了它的 CORS 响应头来允许跨域加载,这会影响是否能跨域加载,却不会影响语法的有效性
修改为:
你必须确保
ww2.example.com
已经正确配置了它的 CORS 响应头来允许跨域加载
总结
这些导入的名称就像是被
let
所定义的
修改为:
这些导入的名称就像是被
const
所定义的
原作在此处的描述不符合之前的文字,并且与实际情况也有偏差。
2017-08-02
第三章 函数
参数默认值表达式
而在
getValue()
的函数声明初次被解析时并不会进行调用。这意味着getValue()
函数若被写为可变的,则它有可能会返回可变的值
修改为:
而在
add()
的函数声明初次被解析时并不会进行调用。这意味着getValue()
函数若被写为可变的,则它返回的值有可能也会变化
名称属性的特殊情况
而使用
Function
构造器创建的函数,其名称属性则会有"anonymous"
前缀
修改为:
而使用
Function
构造器创建的函数,其名称属性为"anonymous"
明确函数的双重用途
当函数未使用
new
进行调用时,[[call]]
方法会被执行
修改为:
当函数未使用
new
进行调用时,[[Call]]
方法会被执行
首字母需要大写。
new.target 元属性
元属性指的是“非对象”(例如
new
)上的一个属性,并提供关联到它的目标的附加信息。当函数的[[Construct]]
方法被调用时,new.target
会被填入new
运算符的作用目标,该目标通常是新创建的对象实例的构造器,并且会成为函数体内部的this
值。
修改为:
元属性指的是“非对象”(例如
new
运算符)上的属性,并提供与之关联的目标的附加信息。当函数的[[Construct]]
方法被调用时,new
运算符的作用目标会填入new.target
元属性,此时函数体内部的this
值是新创建的对象实例,而new.target
的值正是该实例的构造器(构造函数)。
2017-07-04
第二章 字符串与正则表达式
识别子字符串的方法
此处原文对于 endsWith()
方法第二个参数的描述有误。
当提供了第二个参数时,
includes()
与startsWith()
方法会从该索引位置开始尝试匹配;而endsWith()
方法会将字符串长度减去该参数,以此为起点开始尝试匹配。当第二个参数未提供时,includes()
与startsWith()
方法会从字符串起始处开始查找,而endsWith()
方法则从尾部开始。
修改为:
当提供了第二个参数时,
includes()
与startsWith()
方法会从该索引位置开始尝试匹配;而endsWith()
方法则会将该位置减去需搜索文本的长度,在目标位置尝试匹配。当第二个参数未提供时,includes()
与startsWith()
方法会从字符串起始处开始查找,而endsWith()
方法则从尾部开始(事实上是在原字符串长度减去需搜索文本长度的位置上进行正向查找)。
此外,
调用
msg.endsWith("o", 8)
也从位置 4 开始搜索,因为参数8
会从字符串的长度值( 12 )中被减去;
修改为:
调用
msg.endsWith("o", 8)
会在位置 7 进行匹配尝试,因为需要将参数8
减去字符串"o"
的长度1
;
2017-03-26
第二章 字符串与正则表达式
识别子字符串的方法
前三次调用没有使用第二个参数
修改为:
前六次调用没有使用第二个参数
第三章 函数
new.target 元属性
译注:原文此段代码有误。
if (new.target === Person) {
这一行原先写为:
if (typeof new.target === Person) {
原先的写法是有问题的,不能正确发挥作用,它会在
new Person("Nicholas")
这行就抛出错误。
删掉这一段译注。由于向原作者提交 issues 并已经被确认,原文已修改,此段译注就没有必要保留了。
没有 this 绑定
否则,
this
值就会是undefined
。
改为:
否则,
this
值就会是全局对象(在浏览器中是window
,在 nodejs 中是global
)。
第六章 符号与符号属性
使用符号值
这个例子首先使用对象的“需计算字面量属性”方式创建了一个符号类型的属性
firstName
,该属性默认不可枚举,此行为与非符号类型的“需计算字面量属性名”正相反。
修正为:
这个例子首先使用对象的“需计算字面量属性”方式创建了一个符号类型的属性
firstName
,该属性使用 getOwnPropertyDescriptor 查看时显示为可枚举(enumerable: true
),但无法用 for-in 循环遍历,也不会显示在Object.keys()
的结果中。
Symbol.match 、 Symbol.replace 、 Symbol.search 与 Symbol.split
使得开发者无法将自定义对象模拟成正则表达式
稍微添加点附注,让意思更明确:
使得开发者无法将自定义对象模拟成正则表达式(并将它们传递给字符串的这些方法)
此外:
这 4 个符号表示可以将正则表达式作为对应方法的第一个参数传入
修改为:
这 4 个符号表示可以将正则表达式作为字符串对应方法的第一个参数传入
范例代码:
[Symbol.match]: function(value) {
return value.length === 10 ? [value.substring(0, 10)] : null;
},
[Symbol.replace]: function(value, replacement) {
return value.length === 10 ?
replacement + value.substring(10) : value;
},
修改为:
[Symbol.match]: function(value) {
return value.length === 10 ? [value] : null;
},
[Symbol.replace]: function(value, replacement) {
return value.length === 10 ? replacement : value;
},
第七章 Set与Map
创建 Set 并添加项目
(在 Set 内部的比较使用了第四章讨论过的
Object.is()
方法,来判断两个值是否相等)
添加文字,改为:
(在 Set 内部的比较使用了第四章讨论过的
Object.is()
方法,来判断两个值是否相等,唯一的例外是 +0 与 -0 在 Set 中被判断为是相等的)
Set 类型之间的关键差异
- 对于
WeakSet
的实例,只要调用add()
、has()
或delete()
方法时传入了非对象的参数,就会抛出错误;
修改为:
- 对于
WeakSet
的实例,若调用add()
方法时传入了非对象的参数,就会抛出错误(has()
或delete()
则会在传入了非对象的参数时返回false
);
第八章 迭代器与生成器
生成器委托
生成器可以用星号(
*
)配合yield
这一特殊形式来委托另一个生成器
修改为:
生成器可以用星号(
*
)配合yield
这一特殊形式来委托其他的迭代器
解构与 for-of
循环
Map 默认构造器的行为有助于在
for-of
循环中使用解构
修改为
Map 默认迭代器的行为有助于在
for-of
循环中使用解构
第九章 JS的类
基本的类表达式
在匿名的类表达式内
PersonClass.name
是个空的字符串。而若使用了类声明,则PersonClass.name
就会是"PersonClass"
。使用类声明还是类表达式,主要是代码风格问题。相对于函数声明与函数表达式之间的区别,类声明与类表达式都不会被提升,因此对代码运行时的行为影响甚微。唯一显著的差异是匿名类表达式的
name
属性是一个空字符串,而类声明的name
属性则与类名相同(例如,使用类声明时,PersonClass.name
的值为"PersonClass"
)。
修改为(原作者删除了这两段的大量文字):
使用类声明还是类表达式,主要是代码风格问题。相对于函数声明与函数表达式之间的区别,类声明与类表达式都不会被提升,因此对代码运行时的行为影响甚微。
继承内置对象
为了达成这个目的,类的继承模型与 ES5 或更早版本的传统继承模型有轻微差异,体现在以下两个重要方面:
修改为(删除最后一些文字):
为了达成这个目的,类的继承模型与 ES5 或更早版本的传统继承模型有轻微差异:
此外删除后面两段文字的编号。
第十二章 代理与反射接口
起始第二段
ES6 通过增加内置对象使得开发者能进一步接近 JS 引擎的能力。为了让开发者能够创建内置对象,
修改为:
ES6 让开发者能进一步接近 JS 引擎的能力,这些能力原先只存在于内置对象上。
ownKeys 陷阱函数
此节的范例输出有误。
console.log(names.length); // 1
console.log(names[0]); // "proxy"
console.log(keys.length); // 1
console.log(keys[0]); // "proxy"
修正为:
console.log(names.length); // 1
console.log(names[0]); // "name"
console.log(keys.length); // 1
console.log(keys[0]); // "name"
此外删除这段话:
虽然
ownKeys
代理陷阱允许你修改少数操作所返回的键值,但它不能影响一些常用操作,例如for-of
循环以及Object.keys()
方法,这些都是使用代理所无法改变的。