Skip to content
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.

Commit 49a00cf

Browse files
committedMay 1, 2022
feat: update translation
1 parent eeaa6e1 commit 49a00cf

File tree

7 files changed

+85
-46
lines changed

7 files changed

+85
-46
lines changed
 

‎1-js/04-object-basics/09-object-toprimitive/article.md

Lines changed: 32 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -7,11 +7,11 @@ JavaScript 不允许自定义运算符对对象的处理方式。与其他一些
77

88
在此类运算的情况下,对象会被自动转换为原始值,然后对这些原始值进行运算,并得到运算结果(也是一个原始值)。
99

10-
这是一个重要的限制因为 `obj1 + obj2` 的结果不能是另一个对象!
10+
这是一个重要的限制因为 `obj1 + obj2`(或者其他数学运算)的结果不能是另一个对象!
1111

1212
例如,我们无法使用对象来表示向量或矩阵(或成就或其他),把它们相加并期望得到一个“总和”向量作为结果。这样的想法是行不通的。
1313

14-
因此,由于我们无法实现此类运算,所以在实际项目中不存在对对象的数学运算。如果你发现有,那通常是写错了
14+
因此,由于我们从技术上无法实现此类运算,所以在实际项目中不存在对对象的数学运算。如果你发现有,除了极少数例外,通常是写错了
1515

1616
本文将介绍对象是如何转换为原始值的,以及如何对其进行自定义。
1717

@@ -22,17 +22,21 @@ JavaScript 不允许自定义运算符对对象的处理方式。与其他一些
2222

2323
## 转换规则
2424

25-
<info:type-conversions> 一章中,我们已经看到了数值,字符串和布尔转换的规则。但是我们没有讲对象的转换规则。现在我们已经掌握了方法(method)和 symbol 的相关知识,可以开始学习对象原始值转换了。
25+
<info:type-conversions> 一章中,我们已经看到了数字、字符串和布尔转换的规则。但是我们没有讲对象的转换规则。现在我们已经掌握了方法(method)和 symbol 的相关知识,可以开始学习对象原始值转换了。
2626

27-
1. 所有的对象在布尔上下文(context)中均为 `true`。所以对于对象,不存在 to-boolean 转换,只有字符串和数值转换
28-
2. 数值转换发生在对象相减或应用数学函数时。例如,`Date` 对象(将在 <info:date> 一章中介绍)可以相减,`date1 - date2` 的结果是两个日期之间的差值。
27+
1. 没有转换为布尔值。所有的对象在布尔上下文(context)中均为 `true`,就这么简单。只有字符串和数字转换
28+
2. 数字转换发生在对象相减或应用数学函数时。例如,`Date` 对象(将在 <info:date> 一章中介绍)可以相减,`date1 - date2` 的结果是两个日期之间的差值。
2929
3. 至于字符串转换 —— 通常发生在我们像 `alert(obj)` 这样输出一个对象和类似的上下文中。
3030

31-
我们可以使用特殊的对象方法,对字符串和数值转换进行微调
31+
我们可以使用特殊的对象方法,自己实现字符串和数字的转换
3232

33-
类型转换有三种变体(variant),发生在各种情况下
33+
现在让我们一起探究技术细节,因为这是深入讨论该主题的唯一方式
3434

35-
它们被称为 "hint",在 [规范](https://tc39.github.io/ecma262/#sec-toprimitive) 中有详细介绍(译注:当一个对象被用在需要原始值的上下文中时,例如,在 `alert` 或数学运算中,对象会被转换为原始值):
35+
## hint
36+
37+
JavaScript 是如何决定应用哪种转换的?
38+
39+
类型转换在各种情况下有三种变体。它们被称为 "hint",在 [规范](https://tc39.github.io/ecma262/#sec-toprimitive) 所述:
3640

3741
`"string"`
3842
: 对象到字符串的转换,当我们对期望一个字符串的对象执行操作时,如 "alert":
@@ -60,10 +64,12 @@ JavaScript 不允许自定义运算符对对象的处理方式。与其他一些
6064
let greater = user1 > user2;
6165
```
6266

67+
大多数内建的数学函数也包括这种转换。
68+
6369
`"default"`
6470
: 在少数情况下发生,当运算符“不确定”期望值的类型时。
6571

66-
例如,二元加法 `+` 可用于字符串(连接),也可以用于数字(相加),所以字符串和数字这两种类型都可以。因此,当二元加法得到对象类型的参数时,它将依据 `"default"` hint 来对其进行转换。
72+
例如,二元加法 `+` 可用于字符串(连接),也可以用于数字(相加)。因此,当二元加法得到对象类型的参数时,它将依据 `"default"` hint 来对其进行转换。
6773

6874
此外,如果对象被用于与字符串、数字或 symbol 进行 `==` 比较,这时到底应该进行哪种转换也不是很明确,因此使用 `"default"` hint。
6975

@@ -77,21 +83,19 @@ JavaScript 不允许自定义运算符对对象的处理方式。与其他一些
7783

7884
像 `<` 和 `>` 这样的小于/大于比较运算符,也可以同时用于字符串和数字。不过,它们使用 "number" hint,而不是 "default"。这是历史原因。
7985

80-
实际上,我们没有必要记住这些奇特的细节,除了一种情况(`Date` 对象,我们稍后会学到它)之外,所有内建对象都以和 `"number"` 相同的方式实现 `"default"` 转换。我们也可以这样做
86+
上面这些规则看起来比较复杂,但在实践中其实挺简单的
8187

82-
```smart header="没有 `\"boolean\"` hint"
83-
请注意 —— 只有三种 hint。就这么简单。
88+
除了一种情况(`Date` 对象,我们稍后会讲到)之外,所有内建对象都以和 `"number"` 相同的方式实现 `"default"` 转换。我们也可以这样做。
8489

85-
没有 "boolean" hint(在布尔上下文中所有对象都是 `true`)或其他任何东西。如果我们将 `"default"``"number"` 视为相同,就像大多数内建函数一样,那么就只有两种转换了。
86-
```
90+
尽管如此,了解上述的 3 个 hint 还是很重要的,很快你就会明白为什么这样说。
8791

8892
**为了进行转换,JavaScript 尝试查找并调用三个对象方法:**
8993

9094
1. 调用 `obj[Symbol.toPrimitive](hint)` —— 带有 symbol 键 `Symbol.toPrimitive`(系统 symbol)的方法,如果这个方法存在的话,
9195
2. 否则,如果 hint 是 `"string"`
92-
—— 尝试 `obj.toString()` `obj.valueOf()`,无论哪个存在。
96+
—— 尝试调用 `obj.toString()` `obj.valueOf()`,无论哪个存在。
9397
3. 否则,如果 hint 是 `"number"``"default"`
94-
—— 尝试 `obj.valueOf()` `obj.toString()`,无论哪个存在。
98+
—— 尝试调用 `obj.valueOf()` `obj.toString()`,无论哪个存在。
9599

96100
## Symbol.toPrimitive
97101

@@ -128,13 +132,12 @@ alert(user + 500); // hint: default -> 1500
128132

129133
从代码中我们可以看到,根据转换的不同,`user` 变成一个自描述字符串或者一个金额。`user[Symbol.toPrimitive]` 方法处理了所有的转换情况。
130134

131-
132135
## toString/valueOf
133136

134137
如果没有 `Symbol.toPrimitive`,那么 JavaScript 将尝试寻找 `toString``valueOf` 方法:
135138

136-
- 对于 "string" hint:`toString`,如果它不存在, `valueOf`(因此,对于字符串转换,优先 `toString`)。
137-
- 对于其他 hint:`valueOf`,如果它不存在, `toString`(因此,对于数学运算,优先 优先 `valueOf`)。
139+
- 对于 `"string"` hint:调用 `toString` 方法,如果它不存在,则调用 `valueOf` 方法(因此,对于字符串转换,优先调用 `toString`)。
140+
- 对于其他 hint:调用 `valueOf` 方法,如果它不存在,则调用 `toString` 方法(因此,对于数学运算,优先调用 `valueOf` 方法)。
138141

139142
`toString``valueOf` 方法很早己有了。它们不是 symbol(那时候还没有 symbol 这个概念),而是“常规的”字符串命名的方法。它们提供了一种可选的“老派”的实现转换的方法。
140143

@@ -207,23 +210,23 @@ alert(user + 500); // toString -> John500
207210

208211
关于所有原始转换方法,有一个重要的点需要知道,就是它们不一定会返回 "hint" 的原始值。
209212

210-
没有限制 `toString()` 是否返回字符串,或 `Symbol.toPrimitive` 方法是否为 hint "number" 返回数字。
213+
没有限制 `toString()` 是否返回字符串,或 `Symbol.toPrimitive` 方法是否为 `"number"` hint 返回数字。
211214

212215
唯一强制性的事情是:这些方法必须返回一个原始值,而不是对象。
213216

214217
```smart header="历史原因"
215218
由于历史原因,如果 `toString` 或 `valueOf` 返回一个对象,则不会出现 error,但是这种值会被忽略(就像这种方法根本不存在)。这是因为在 JavaScript 语言发展初期,没有很好的 "error" 的概念。
216219
217-
相反,`Symbol.toPrimitive` **必须** 返回一个原始值,否则就会出现 error。
220+
相反,`Symbol.toPrimitive` 更严格,它 **必须** 返回一个原始值,否则就会出现 error。
218221
```
219222

220223
## 进一步的转换
221224

222225
我们已经知道,许多运算符和函数执行类型转换,例如乘法 `*` 将操作数转换为数字。
223226

224-
如果我们将对象作为参数传递,则会出现两个阶段
227+
如果我们将对象作为参数传递,则会出现两个运算阶段
225228
1. 对象被转换为原始值(通过前面我们描述的规则)。
226-
2. 如果生成的原始值的类型不正确,则继续进行转换
229+
2. 如果还需要进一步计算,则生成的原始值会被进一步转换
227230

228231
例如:
229232

@@ -260,18 +263,18 @@ alert(obj + 2); // 22("2" + 2)被转换为原始值字符串 => 级联
260263
这里有三种类型(hint):
261264
- `"string"`(对于 `alert` 和其他需要字符串的操作)
262265
- `"number"`(对于数学运算)
263-
- `"default"`(少数运算符)
266+
- `"default"`(少数运算符,通常对象以和 `"number"` 相同的方式实现 `"default"` 转换
264267

265-
规范明确描述了哪个运算符使用哪个 hint。很少有运算符“不知道期望什么”并使用 `"default"` hint。通常对于内建对象,`"default"` hint 的处理方式与 `"number"` 相同,因此在实践中,最后两个 hint 常常合并在一起。
268+
规范明确描述了哪个运算符使用哪个 hint。
266269

267270
转换算法是:
268271

269272
1. 调用 `obj[Symbol.toPrimitive](hint)` 如果这个方法存在,
270273
2. 否则,如果 hint 是 `"string"`
271-
- 尝试 `obj.toString()` `obj.valueOf()`,无论哪个存在。
274+
- 尝试调用 `obj.toString()` `obj.valueOf()`,无论哪个存在。
272275
3. 否则,如果 hint 是 `"number"` 或者 `"default"`
273-
- 尝试 `obj.valueOf()` `obj.toString()`,无论哪个存在。
276+
- 尝试调用 `obj.valueOf()` `obj.toString()`,无论哪个存在。
274277

275-
在实践中,为了便于进行日志记录或调试,对于应该返回一种“可读性好”的对象的表示形式的转换,只需要实现 `obj.toString()` 作为字符串转换的“全能”方法就足够了
278+
所有这些方法都必须返回一个原始值才能工作(如果已定义)
276279

277-
对于数学运算,JavaScript 没有提供通过使用某些方法“覆盖”它们的方式,因此,在实际的项目中,很少对对象进行数学运算
280+
在实际使用中,通常只实现 `obj.toString()` 作为字符串转换的“全能”方法就足够了,该方法应该返回对象的“人类可读”表示,用于日志记录或调试

‎1-js/05-data-types/02-number/article.md

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ let billion = 1000000000;
2222
let billion = 1_000_000_000;
2323
```
2424

25-
这里的下划线 `_` 扮演了“语法糖”的角色,使得数字具有更强的可读性。JavaScript 引擎会直接忽略数字之间的 `_`,所以 上面两个例子其实是一样的。
25+
这里的下划线 `_` 扮演了“[语法糖](https://en.wikipedia.org/wiki/Syntactic_sugar)”的角色,使得数字具有更强的可读性。JavaScript 引擎会直接忽略数字之间的 `_`,所以 上面两个例子其实是一样的。
2626

2727
但在现实生活中,我们通常会尽量避免写带一长串零的数。因为我们比较懒……我们会尝试将 10 亿写成 `"1bn"`,或将 73 亿写成 `"7.3bn"`。对于大多数大的数字来说都是如此。
2828

@@ -118,6 +118,7 @@ alert( num.toString(2) ); // 11111111
118118
如果我们放置一个点:`123456.toString(36)`,那么就会出现一个 error,因为 JavaScript 语法隐含了第一个点之后的部分为小数部分。如果我们再放一个点,那么 JavaScript 就知道小数部分为空,现在使用该方法。
119119
120120
也可以写成 `(123456).toString(36)`
121+
121122
```
122123

123124
## 舍入
@@ -156,7 +157,7 @@ alert( num.toString(2) ); // 11111111
156157

157158
1. 乘除法
158159

159-
例如,要将数字舍入到小数点后两位,我们可以将数字乘以 `100`(或更大的 10 的整数次幂),调用舍入函数,然后再将其除回。
160+
例如,要将数字舍入到小数点后两位,我们可以将数字乘以 `100`,调用舍入函数,然后再将其除回。
160161
```js run
161162
let num = 1.23456;
162163
@@ -184,21 +185,21 @@ alert( num.toString(2) ); // 11111111
184185
alert( num.toFixed(5) ); // "12.34000",在结尾添加了 0,以达到小数点后五位
185186
```
186187

187-
我们可以使用一元加号或 `Number()` 调用,将其转换为数字`+ num.toFixed(5)`
188+
我们可以使用一元加号或 `Number()` 调用,将其转换为数字,例如 `+ num.toFixed(5)`
188189

189190
## 不精确的计算
190191

191192
在内部,数字是以 64 位格式 [IEEE-754](http://en.wikipedia.org/wiki/IEEE_754-1985) 表示的,所以正好有 64 位可以存储一个数字:其中 52 位被用于存储这些数字,其中 11 位用于存储小数点的位置(对于整数,它们为零),而 1 位用于符号。
192193

193-
如果一个数字太大,则会溢出 64 位存储,并可能会导致无穷大
194+
如果一个数字真的很大,则可能会溢出 64 位存储,变成一个特殊的数值 `Infinity`
194195

195196
```js run
196197
alert( 1e500 ); // Infinity
197198
```
198199

199200
这可能不那么明显,但经常会发生的是,精度的损失。
200201

201-
考虑下这个(falsy!)测试
202+
考虑下这个(falsy!)相等性测试
202203

203204
```js run
204205
alert( 0.1 + 0.2 == 0.3 ); // *!*false*/!*
@@ -212,13 +213,13 @@ alert( 0.1 + 0.2 == 0.3 ); // *!*false*/!*
212213
alert( 0.1 + 0.2 ); // 0.30000000000000004
213214
```
214215

215-
哎哟!这个错误比不正确的比较的后果更严重。想象一下,你创建了一个电子购物网站,如果访问者将价格为 `¥ 0.10``¥ 0.20` 的商品放入了他的购物车。订单总额将是 `¥ 0.30000000000000004`。这会让任何人感到惊讶。
216+
我擦!想象一下,你创建了一个电子购物网站,如果访问者将价格为 `¥ 0.10``¥ 0.20` 的商品放入了他的购物车。订单总额将是 `¥ 0.30000000000000004`。这会让任何人感到惊讶。
216217

217218
但为什么会这样呢?
218219

219220
一个数字以其二进制的形式存储在内存中,一个 10 的序列。但是在十进制数字系统中看起来很简单的 `0.1``0.2` 这样的小数,实际上在二进制形式中是无限循环小数。
220221

221-
换句话说,什么是 `0.1``0.1` 就是 `1` 除以 `10``1/10`,即十分之一。在十进制数字系统中,这样的数字表示起来很容易。将其与三分之一进行比较:`1/3`。三分之一变成了无限循环小数 `0.33333(3)`
222+
什么是 `0.1``0.1` 就是 `1` 除以 `10``1/10`,即十分之一。在十进制数字系统中,这样的数字表示起来很容易。将其与三分之一进行比较:`1/3`。三分之一变成了无限循环小数 `0.33333(3)`
222223

223224
在十进制数字系统中,可以保证以 `10` 的整数次幂作为除数能够正常工作,但是以 `3` 作为除数则不能。也是同样的原因,在二进制数字系统中,可以保证以 `2` 的整数次幂作为除数时能够正常工作,但 `1/10` 就变成了一个无限循环的二进制小数。
224225

@@ -331,8 +332,7 @@ alert( isFinite(num) );
331332
请注意,在所有数字函数中,包括 `isFinite`,空字符串或仅有空格的字符串均被视为 `0`
332333

333334
```smart header="与 `Object.is` 进行比较"
334-
335-
有一个特殊的内建方法 [`Object.is`](mdn:js/Object/is),它类似于 `===` 一样对值进行比较,但它对于两种边缘情况更可靠:
335+
有一个特殊的内建方法 `Object.is`,它类似于 `===` 一样对值进行比较,但它对于两种边缘情况更可靠:
336336
337337
1. 它适用于 `NaN``Object.is(NaNNaN) === true`,这是件好事。
338338
2. 值 `0``-0` 是不同的:`Object.is(0-0) === false`,从技术上讲这是对的,因为在内部,数字的符号位可能会不同,即使其他所有位均为零。

‎1-js/05-data-types/04-array/3-call-array-this/task.md

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,7 @@ let arr = ["a", "b"];
1111

1212
arr.push(function() {
1313
alert( this );
14-
})
14+
});
1515

1616
arr[2](); // ?
1717
```
18-

‎1-js/05-data-types/04-array/article.md

Lines changed: 35 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,38 @@ let fruits = [
9292
因为每一行都是相似的,所以这种以“逗号结尾”的方式使得插入/移除项变得更加简单。
9393
````
9494

95+
## 使用 "at" 获取最后一个元素
96+
97+
[recent browser="new"]
98+
99+
假设我们想要数组的最后一个元素。
100+
101+
一些编程语言允许我们使用负数索引来实现这一点,例如 `fruits[-1]`
102+
103+
但在 JavaScript 中这行不通。结果将是 `undefined`,因为方括号中的索引是被按照其字面意思处理的。
104+
105+
我们可以显式地计算最后一个元素的索引,然后访问它:`fruits[fruits.length - 1]`
106+
107+
```js run
108+
let fruits = ["Apple", "Orange", "Plum"];
109+
110+
alert( fruits[fruits.length-1] ); // Plum
111+
```
112+
113+
有点麻烦,不是吗?我们需要写两次变量名。
114+
115+
幸运的是,这里有一个更简短的语法 `fruits.at(-1)`
116+
117+
```js run
118+
let fruits = ["Apple", "Orange", "Plum"];
119+
120+
// 与 fruits[fruits.length-1] 相同
121+
alert( fruits.at(-1) ); // Plum
122+
```
123+
124+
换句话说,`arr.at(i)`
125+
- 如果 `i >= 0`,则与 `arr[i]` 完全相同。
126+
- 对于 `i` 为负数的情况,它则从数组的尾部向前数。
95127

96128
## pop/push, shift/unshift 方法
97129

@@ -138,6 +170,8 @@ JavaScript 中的数组既可以用作队列,也可以用作栈。它们允许
138170
alert( fruits ); // Apple, Orange
139171
```
140172

173+
`fruits.pop()` 和 `fruits.at(-1)` 都返回数组的最后一个元素,但 `fruits.pop()` 同时也删除了数组的最后一个元素,进而修改了原数组。
174+
141175
`push`
142176
: 在数组末端添加元素:
143177

@@ -439,7 +473,7 @@ JavaScript 中的数组与其它一些编程语言的不同,不应该使用 `=
439473
- 如果 `==` 左右两个参数之中有一个参数是对象,另一个参数是原始类型,那么该对象将会被转换为原始类型,转换规则如 <info:object-toprimitive> 一章所述。
440474
- ……`null``undefined` 相等 `==`,且各自不等于任何其他的值。
441475

442-
严格比较 `===` 更简单,因为它不会进行类型转换。
476+
严格比较 `===` 更简单,因为它不会进行类型转换。
443477

444478
所以,如果我们使用 `==` 来比较数组,除非我们比较的是两个引用同一数组的变量,否则它们永远不相等。
445479

‎1-js/05-data-types/05-array-methods/3-filter-range-in-place/_js.view/test.js

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,13 +4,13 @@ describe("filterRangeInPlace", function() {
44

55
let arr = [5, 3, 8, 1];
66

7-
filterRangeInPlace(arr, 1, 4);
7+
filterRangeInPlace(arr, 2, 5);
88

9-
assert.deepEqual(arr, [3, 1]);
9+
assert.deepEqual(arr, [5, 3]);
1010
});
1111

1212
it("doesn't return anything", function() {
1313
assert.isUndefined(filterRangeInPlace([1,2,3], 1, 4));
1414
});
1515

16-
});
16+
});

‎1-js/05-data-types/10-destructuring-assignment/article.md

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,7 @@ alert( title ); // Consul
6969
````
7070

7171
````smart header="等号右侧可以是任何可迭代对象"
72+
7273
……实际上,我们可以将其与任何可迭代对象一起使用,而不仅限于数组:
7374
7475
```js
@@ -78,6 +79,7 @@ let [one, two, three] = new Set([1, 2, 3]);
7879
这行得通,因为在内部,解构赋值是通过迭代右侧的值来完成工作的。这是一种用于对在 `=` 右侧的值上调用 `for..of` 并进行赋值的操作的语法糖。
7980
````
8081

82+
8183
````smart header="赋值给等号左侧的任何内容"
8284
我们可以在等号左侧使用任何“可以被赋值的”东西。
8385
@@ -89,6 +91,7 @@ let user = {};
8991
alert(user.name); // John
9092
alert(user.surname); // Smith
9193
```
94+
9295
````
9396

9497
````smart header="与 .entries() 方法进行循环操作"
@@ -426,7 +429,7 @@ let options = {
426429
height: 200
427430
},
428431
items: ["Cake", "Donut"],
429-
extra: true
432+
extra: true
430433
};
431434

432435
// 为了清晰起见,解构赋值语句被写成多行的形式

‎1-js/07-object-properties/02-property-accessors/article.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55

66
第一种是 **数据属性**。我们已经知道如何使用它们了。到目前为止,我们使用过的所有属性都是数据属性。
77

8-
第二种类型的属性是新东西。它是 **访问器属性(accessor properties**。它们本质上是用于获取和设置值的函数,但从外部代码来看就像常规属性。
8+
第二种类型的属性是新东西。它是 **访问器属性(accessor property**。它们本质上是用于获取和设置值的函数,但从外部代码来看就像常规属性。
99

1010
## getter 和 setter
1111

0 commit comments

Comments
 (0)
Please sign in to comment.