Skip to content

Commit 5347181

Browse files
authored
Merge pull request #71 from lenchen1112/master
Sync with upstream @b43e954
2 parents 76544a9 + 4c8946b commit 5347181

File tree

18 files changed

+123
-80
lines changed

18 files changed

+123
-80
lines changed

1-js/02-first-steps/12-while-for/article.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -211,7 +211,7 @@ for (;;) {
211211

212212
例如,底下的迴圈詢問使用者一連串的數字,當沒有數字被輸入時將會 "中斷(breaking)":
213213

214-
```js
214+
```js run
215215
let sum = 0;
216216

217217
while (true) {

1-js/04-object-basics/05-object-toprimitive/article.md

Lines changed: 63 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -45,21 +45,27 @@
4545
`"default"`
4646
: 發生於極少情況下,當 "不確定" 運算子會預期是什麼類型時。
4747

48-
舉個例,二元加法 `+` 可以同時運作在字串(串接)和數值(相加)上,所以字串與數值兩者皆可使用。或當物件使用 `==` 比較字串、數值或符號時,哪種轉換會進行也很不清楚。
48+
舉個例,二元加法 `+` 可以同時運作在字串(串接)和數值(相加)上,所以字串與數值兩者皆可使用。因此若二元加法以物件作為引數時,它會使用 `"default"` 提示來轉換。
49+
50+
同樣地,若物件使用 `==` 比較字串、數值或符號時,哪種轉換會進行也很不清楚,所以會使用 `"default"` 提示。
4951

5052
```js
51-
// 二元加法
52-
let total = car1 + car2;
53+
// 使用 "default" 提示的二元加法
54+
let total = obj1 + obj2;
5355

54-
// obj == string/number/symbol
56+
// obj == number 使用 "default" 提示
5557
if (user == 1) { ... };
5658
```
5759

58-
大於/小於 運算子 `<>` 一樣可以同時作用於字串和數值上。但它使用 "number" 提示而非 "default",這是因為歷史因素。
60+
大於/小於 運算子 `<>` 一樣可以同時作用於字串和數值上。但它仍使用 "number" 提示而非 "default",這是因為歷史因素。
61+
62+
實際上,我們不需要記住這些罕見的細節,因為除了某種例外(`Date` 物件,我們晚點會學到),所有內建物件都實作了和 `"number"` 一樣的 `"default"` 轉換,所以可以同樣方式運作。
5963

60-
實際上,除了某種物件(`Date` 物件,我們晚點會學到),所有內建物件都實作了和 `"number"` 一樣的 `"default"` 轉換,且我們或許也該這樣做。
64+
```smart header="No `\"boolean\"` hint"
65+
請注意 -- 只有三種提示,就那麼簡單。
6166

62-
請注意 -- 只有三種提示,就那麼簡單,不存在 "布林" 提示(所有物件在布林上下文中都是 `true`)等其它的。且若我們對 `"default"``"number"` 一視同仁,如同大多內建物件那樣,那將只有兩種轉換了。
67+
不存在 "布林" 提示(所有物件在布林上下文中都是 `true`)等等其它的轉換。且若我們將 `"default"``"number"` 一視同仁,如同大多內建物件那樣,那將只有兩種轉換了。
68+
```
6369
6470
**要做轉換時,JavaScript 試著找尋並呼叫三種物件方法:**
6571
@@ -110,7 +116,29 @@ alert(user + 500); // hint: default -> 1500
110116
- 對於 "string" 提示,`toString -> valueOf`
111117
- 否則,`valueOf -> toString`
112118

113-
舉個例,這個 `user` 採用 `toString``valueOf` 做了如同前述的事情:
119+
這些方法必須回傳一個原生值,若 `toString``valueOf` 回傳一個物件,則它將被忽略(視同根本不存在這個方法)。
120+
121+
每個物件會有下列預設的 `toString``valueOf` 方法:
122+
123+
- `toString` 方法回傳一個字串 `"[object Object]"`
124+
- `valueOf` 方法回傳物件自身。
125+
126+
在此演示一下:
127+
128+
```js run
129+
let user = {name: "John"};
130+
131+
alert(user); // [object Object]
132+
alert(user.valueOf() === user); // true
133+
```
134+
135+
所以若我們試圖將某個物件作為字串使用,像是用於 `alert` 等其它地方,那我們預設將看到 `[object Object]`
136+
137+
為了避免搞混,而在這邊提到預設的 `valueOf` 只是為了完整性考量。如同你所見,它回傳了該物件本身從而被忽略了。別問我為什麼,那是歷史因素。所以我們可以假設它根本不存在。
138+
139+
來實作這些方法吧。
140+
141+
舉個例,這個 `user` 使用 `toString``valueOf` 的組合做了如同上述的事,而不是用 `Symbol.toPrimitive`
114142

115143
```js run
116144
let user = {
@@ -167,35 +195,41 @@ alert(user + 500); // toString -> John500
167195
相對地,`Symbol.toPrimitive` *必須* 回傳一個原生類型,否則就會產生錯誤。
168196
```
169197

170-
## 進一步的操作
198+
## 進一步的轉換
171199

172-
一個發起轉換的運算獲得了原生類型,然後繼續運作,並在需要時再套用進一步的轉換。
200+
如同我們所知的,許多運算子與函式都會做類型轉換,像是:乘法 `*` 會轉換運算元為數值。
201+
202+
若我們傳入一個物件作為引數,那將會進行兩個步驟:
203+
1. 物件被轉為原生值(使用上述的規則)。
204+
2. 若轉完的原生值並非正確類型,就再進行轉換。
173205

174206
舉個例:
175207

176-
- 數學運算,除了二元加法以外,將會轉換該原生類型為數值:
208+
```js run
209+
let obj = {
210+
// 在少了其他方法的情況下,toString 處理所有的轉換
211+
toString() {
212+
return "2";
213+
}
214+
};
177215

178-
```js run
179-
let obj = {
180-
// 在少了其他方法的情況下,toString 處理所有的轉換
181-
toString() {
182-
return "2";
183-
}
184-
};
216+
alert(obj * 2); // 4,物件被轉為原生類型 "2",然後乘法會讓它再變成一個數值
217+
```
185218

186-
alert(obj * 2); // 4,物件被轉為原生類型 "2",然後乘法會讓它再變成一個數值
187-
```
219+
1. `obj * 2` 的乘法運算會先轉換該物件為原生值(也就是變成一個字串 `"2"`)。
220+
2. 然後 `"2" * 2` 會變成 `2 * 2`(字串被轉換為數值)。
188221

189-
- 二元加法在同樣情境時會連接字串:
190-
```js run
191-
let obj = {
192-
toString() {
193-
return "2";
194-
}
195-
};
222+
二元加法在同樣情境時會連接字串,並欣然接受轉完的字串:
196223

197-
alert(obj + 2); // 22(轉換成原生類型會回傳一個字串 => 串接)
198-
```
224+
```js run
225+
let obj = {
226+
toString() {
227+
return "2";
228+
}
229+
};
230+
231+
alert(obj + 2); // 22("2" + 2),轉換為原生值時回傳一個字串 => 串接
232+
```
199233

200234
## 總結
201235

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,3 @@
11

22
- Please note how methods are stored. They are simply added to `this.methods` property.
33
- All tests and numeric conversions are done in the `calculate` method. In future it may be extended to support more complex expressions.
4-
5-
[js src="_js/solution.js"]

1-js/05-data-types/06-iterable/article.md

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -142,15 +142,17 @@ for (let char of str) {
142142

143143
For deeper understanding let's see how to use an iterator explicitly.
144144

145-
We'll iterate over a string in exactlly the same way as `for..of`, but with direct calls. This code creates a string iterator and gets values from it "manually":
145+
We'll iterate over a string in exactly the same way as `for..of`, but with direct calls. This code creates a string iterator and gets values from it "manually":
146146

147147
```js run
148148
let str = "Hello";
149149

150150
// does the same as
151151
// for (let char of str) alert(char);
152152

153+
*!*
153154
let iterator = str[Symbol.iterator]();
155+
*/!*
154156

155157
while (true) {
156158
let result = iterator.next();
@@ -212,7 +214,7 @@ let arr = Array.from(arrayLike); // (*)
212214
alert(arr.pop()); // World (method works)
213215
```
214216

215-
`Array.from` at the line `(*)` takes the object, examines it for being an iterable or array-like, then makes a new array and copies there all items.
217+
`Array.from` at the line `(*)` takes the object, examines it for being an iterable or array-like, then makes a new array and copies all items to it.
216218

217219
The same happens for an iterable:
218220

@@ -268,7 +270,7 @@ for (let char of str) {
268270
alert(chars);
269271
```
270272

271-
...But is shorter.
273+
...But it is shorter.
272274

273275
We can even build surrogate-aware `slice` on it:
274276

1-js/06-advanced-functions/08-settimeout-setinterval/article.md

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,7 @@ So, this will also work:
6161
setTimeout("alert('Hello')", 1000);
6262
```
6363

64-
But using strings is not recommended, use functions instead of them, like this:
64+
But using strings is not recommended, use arrow functions instead of them, like this:
6565

6666
```js run no-beautify
6767
setTimeout(() => alert('Hello'), 1000);
@@ -184,7 +184,7 @@ Let's compare two code fragments. The first one uses `setInterval`:
184184
```js
185185
let i = 1;
186186
setInterval(function() {
187-
func(i);
187+
func(i++);
188188
}, 100);
189189
```
190190

@@ -193,12 +193,12 @@ The second one uses nested `setTimeout`:
193193
```js
194194
let i = 1;
195195
setTimeout(function run() {
196-
func(i);
196+
func(i++);
197197
setTimeout(run, 100);
198198
}, 100);
199199
```
200200

201-
For `setInterval` the internal scheduler will run `func(i)` every 100ms:
201+
For `setInterval` the internal scheduler will run `func(i++)` every 100ms:
202202

203203
![](setinterval-interval.svg)
204204

1-js/06-advanced-functions/10-bind/article.md

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -83,10 +83,12 @@ let user = {
8383

8484
setTimeout(() => user.sayHi(), 1000);
8585

86-
// ...within 1 second
87-
user = { sayHi() { alert("Another user in setTimeout!"); } };
86+
// ...the value of user changes within 1 second
87+
user = {
88+
sayHi() { alert("Another user in setTimeout!"); }
89+
};
8890

89-
// Another user in setTimeout?!?
91+
// Another user in setTimeout!
9092
```
9193

9294
The next solution guarantees that such thing won't happen.
@@ -159,9 +161,16 @@ let user = {
159161
let sayHi = user.sayHi.bind(user); // (*)
160162
*/!*
161163

164+
// can run it without an object
162165
sayHi(); // Hello, John!
163166

164167
setTimeout(sayHi, 1000); // Hello, John!
168+
169+
// even if the value of user changes within 1 second
170+
// sayHi uses the pre-bound value
171+
user = {
172+
sayHi() { alert("Another user in setTimeout!"); }
173+
};
165174
```
166175

167176
In the line `(*)` we take the method `user.sayHi` and bind it to `user`. The `sayHi` is a "bound" function, that can be called alone or passed to `setTimeout` -- doesn't matter, the context will be right.

1-js/09-classes/02-class-inheritance/article.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ So we can create new functionality on top of the existing.
77

88
## The "extends" keyword
99

10-
Let's say with have class `Animal`:
10+
Let's say we have class `Animal`:
1111

1212
```js
1313
class Animal {
@@ -40,7 +40,7 @@ The syntax to extend another class is: `class Child extends Parent`.
4040

4141
Let's create `class Rabbit` that inherits from `Animal`:
4242

43-
```js
43+
```js run
4444
*!*
4545
class Rabbit extends Animal {
4646
*/!*

1-js/09-classes/06-instanceof/article.md

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
The `instanceof` operator allows to check whether an object belongs to a certain class. It also takes inheritance into account.
44

5-
Such a check may be necessary in many cases, here we'll use it for building a *polymorphic* function, the one that treats arguments differently depending on their type.
5+
Such a check may be necessary in many cases. Here we'll use it for building a *polymorphic* function, the one that treats arguments differently depending on their type.
66

77
## The instanceof operator [#ref-instanceof]
88

@@ -46,7 +46,7 @@ alert( arr instanceof Object ); // true
4646

4747
Please note that `arr` also belongs to the `Object` class. That's because `Array` prototypically inherits from `Object`.
4848

49-
Normally, `instanceof` operator examines the prototype chain for the check. We can also set a custom logic in the static method `Symbol.hasInstance`.
49+
Normally, `instanceof` examines the prototype chain for the check. We can also set a custom logic in the static method `Symbol.hasInstance`.
5050

5151
The algorithm of `obj instanceof Class` works roughly as follows:
5252

@@ -68,7 +68,7 @@ The algorithm of `obj instanceof Class` works roughly as follows:
6868
alert(obj instanceof Animal); // true: Animal[Symbol.hasInstance](obj) is called
6969
```
7070

71-
2. Most classes do not have `Symbol.hasInstance`. In that case, the standard logic is used: `obj instanceOf Class` checks whether `Class.prototype` equals to one of prototypes in the `obj` prototype chain.
71+
2. Most classes do not have `Symbol.hasInstance`. In that case, the standard logic is used: `obj instanceOf Class` checks whether `Class.prototype` is equal to one of the prototypes in the `obj` prototype chain.
7272

7373
In other words, compare one after another:
7474
```js
@@ -107,7 +107,7 @@ By the way, there's also a method [objA.isPrototypeOf(objB)](mdn:js/object/isPro
107107

108108
It's funny, but the `Class` constructor itself does not participate in the check! Only the chain of prototypes and `Class.prototype` matters.
109109
110-
That can lead to interesting consequences when `prototype` property is changed after the object is created.
110+
That can lead to interesting consequences when a `prototype` property is changed after the object is created.
111111
112112
Like here:
113113
@@ -186,7 +186,7 @@ let user = {
186186
alert( {}.toString.call(user) ); // [object User]
187187
```
188188
189-
For most environment-specific objects, there is such a property. Here are few browser specific examples:
189+
For most environment-specific objects, there is such a property. Here are some browser specific examples:
190190
191191
```js run
192192
// toStringTag for the environment-specific object and class:

1-js/11-async/04-promise-error-handling/article.md

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -166,11 +166,11 @@ new Promise(function() {
166166

167167
In case of an error, the promise becomes rejected, and the execution should jump to the closest rejection handler. But there is none. So the error gets "stuck". There's no code to handle it.
168168

169-
In practice, just like with regular unhandled errors in code, it means that something has terribly gone wrong.
169+
In practice, just like with regular unhandled errors in code, it means that something has gone terribly wrong.
170170

171-
What happens when a regular error occurs and is not caught by `try..catch`? The script dies with a message in console. Similar thing happens with unhandled promise rejections.
171+
What happens when a regular error occurs and is not caught by `try..catch`? The script dies with a message in the console. A similar thing happens with unhandled promise rejections.
172172

173-
JavaScript engine tracks such rejections and generates a global error in that case. You can see it in the console if you run the example above.
173+
The JavaScript engine tracks such rejections and generates a global error in that case. You can see it in the console if you run the example above.
174174

175175
In the browser we can catch such errors using the event `unhandledrejection`:
176176

@@ -201,4 +201,4 @@ In non-browser environments like Node.js there are other ways to track unhandled
201201
- `.catch` handles errors in promises of all kinds: be it a `reject()` call, or an error thrown in a handler.
202202
- We should place `.catch` exactly in places where we want to handle errors and know how to handle them. The handler should analyze errors (custom error classes help) and rethrow unknown ones (maybe they are programming mistakes).
203203
- It's ok not to use `.catch` at all, if there's no way to recover from an error.
204-
- In any case we should have the `unhandledrejection` event handler (for browsers, and analogs for other environments), to track unhandled errors and inform the user (and probably our server) about the them, so that our app never "just dies".
204+
- In any case we should have the `unhandledrejection` event handler (for browsers, and analogs for other environments) to track unhandled errors and inform the user (and probably our server) about them, so that our app never "just dies".

0 commit comments

Comments
 (0)