Skip to content

Update some chapters to [Jul 17, 2020] Part-4 #768

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Jul 18, 2020
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion 1-js/05-data-types/02-number/article.md
Original file line number Diff line number Diff line change
@@ -148,7 +148,7 @@ alert( num.toString(2) ); // 11111111

1. 乘除法

例如,要将数字舍入到小数点后两位,我们可以将数字乘以 `100`,调用舍入函数,然后再将其除回。
例如,要将数字舍入到小数点后两位,我们可以将数字乘以 `100`(或更大的 10 的整数次幂),调用舍入函数,然后再将其除回。
```js run
let num = 1.23456;

19 changes: 19 additions & 0 deletions 1-js/05-data-types/10-destructuring-assignment/article.md
Original file line number Diff line number Diff line change
@@ -121,6 +121,25 @@ for (let [key, value] of user) {
}
```
````

```smart header="Swap variables trick"
A well-known trick for swapping values of two variables:

```js run
let guest = "Jane";
let admin = "Pete";

// Swap values: make guest=Pete, admin=Jane
[guest, admin] = [admin, guest];

alert(`${guest} ${admin}`); // Pete Jane (successfully swapped!)
```

Here we create a temporary array of two variables and immediately destructure it in swapped order.

We can swap more than two variables this way.


### 剩余的 '...'

如果我们不只是要获得第一个值,还要将后续的所有元素都收集起来 — 我们可以使用三个点 `"..."` 来再加一个参数来接收“剩余的”元素:
2 changes: 2 additions & 0 deletions 1-js/05-data-types/11-date/6-get-seconds-today/solution.md
Original file line number Diff line number Diff line change
@@ -23,4 +23,6 @@ function getSecondsToday() {
let d = new Date();
return d.getHours() * 3600 + d.getMinutes() * 60 + d.getSeconds();
};

alert( getSecondsToday() );
```
Original file line number Diff line number Diff line change
@@ -225,11 +225,11 @@ alert( Array.from(str) ); // H,e,l,l,o
因此,对于将一些“东西”转换为数组的任务,`Array.from` 往往更通用。


## 获取一个 object/array 的副本
## 获取一个 array/object 的副本

还记得我们 [之前讲过的](https://zh.javascript.info/object#fu-zhi-he-he-bing-objectassign) `Object.assign()` 吗?
还记得我们 [之前讲过的](info:object-copy#fu-zhi-he-he-bing-objectassign) `Object.assign()` 吗?

使用 spread 操作符也可以做同样的事情
使用 spread 语法也可以做同样的事情

```js run
let arr = [1, 2, 3];
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
function byField(fieldName){
return (a, b) => a[fieldName] > b[fieldName] ? 1 : -1;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
function byField(fieldName){

// Your code goes here.

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
describe("byField", function(){

let users = [
{ name: "John", age: 20, surname: "Johnson" },
{ name: "Pete", age: 18, surname: "Peterson" },
{ name: "Ann", age: 19, surname: "Hathaway" },
];

it("sorts users by name", function(){
let nameSortedKey = [
{ name: "Ann", age: 19, surname: "Hathaway" },
{ name: "John", age: 20, surname: "Johnson"},
{ name: "Pete", age: 18, surname: "Peterson" },
];
let nameSortedAnswer = users.sort(byField("name"));
assert.deepEqual(nameSortedKey, nameSortedAnswer);
});

it("sorts users by age", function(){
let ageSortedKey = [
{ name: "Pete", age: 18, surname: "Peterson" },
{ name: "Ann", age: 19, surname: "Hathaway" },
{ name: "John", age: 20, surname: "Johnson"},
];
let ageSortedAnswer = users.sort(byField("age"));
assert.deepEqual(ageSortedKey, ageSortedKey);
});

it("sorts users by surname", function(){
let surnameSortedKey = [
{ name: "Ann", age: 19, surname: "Hathaway" },
{ name: "John", age: 20, surname: "Johnson"},
{ name: "Pete", age: 18, surname: "Peterson" },
];
let surnameSortedAnswer = users.sort(byField("surname"));
assert.deepEqual(surnameSortedAnswer, surnameSortedKey);
});

});
21 changes: 0 additions & 21 deletions 1-js/06-advanced-functions/03-closure/9-sort-by-field/solution.md
Original file line number Diff line number Diff line change
@@ -1,22 +1 @@


```js run
let users = [
{ name: "John", age: 20, surname: "Johnson" },
{ name: "Pete", age: 18, surname: "Peterson" },
{ name: "Ann", age: 19, surname: "Hathaway" }
];

*!*
function byField(field) {
return (a, b) => a[field] > b[field] ? 1 : -1;
}
*/!*

users.sort(byField('name'));
users.forEach(user => alert(user.name)); // Ann, John, Pete

users.sort(byField('age'));
users.forEach(user => alert(user.name)); // Pete, Ann, John
```

45 changes: 28 additions & 17 deletions 1-js/06-advanced-functions/04-var/article.md
Original file line number Diff line number Diff line change
@@ -13,27 +13,18 @@
2. `const`
3. `var`

`let` 和 `const` 在词法环境中的行为完全一样。

但 `var` 却是一头完全不同的,源自非常古老的时代的怪兽。在现代脚本中一般不再使用它,但它仍然潜伏在旧脚本中。

如果你不打算接触这样的脚本,你甚至可以跳过本章或推迟阅读本章,但是之后你很可能会踩到它的坑。

乍一看,`var` 和 `let` 的行为相似,不就是声明变量嘛:
The `var` declaration is similar to `let`. Most of the time we can replace `let` by `var` or vice-versa and expect things to work:

```js run
function sayHi() {
 var phrase = "Hello"; // 局部变量,使用 "var",而不是 "let"

alert(phrase); // Hello
}
var message = "Hi";
alert(message); // Hi
```

sayHi();
But internally `var` is a very different beast, that originates from very old times. It's generally not used in modern scripts, but still lurks in the old ones.

alert(phrase); // Error, phrase is not defined
```
If you don't plan on meeting such scripts you may even skip this chapter or postpone it.

……但两者存在区别。
On the other hand, it's important to understand differences when migrating old scripts from `var` to `let`, to avoid odd errors.

## "var" 没有块级作用域

@@ -94,7 +85,27 @@ alert(phrase); // Error: phrase is not defined (Check the Developer Console)

可以看到,`var` 穿透了 `if`,`for` 和其它代码块。这是因为在早期的 JavaScript 中,块没有词法环境。而 `var` 就是这个时期的代表之一。

## "var" 声明在函数开头就会被处理
## "var" 允许重新声明

If we declare the same variable with `let` twice in the same scope, that's an error:

```js run
let user;
let user; // SyntaxError: 'user' has already been declared
```

With `var`, we can redeclare a variable any number of times. If we use `var` with an already-declared variable, it's just ignored:

```js run
var user = "Pete";

var user = "John"; // this "var" does nothing (already declared)
// ...it doesn't trigger an error

alert(user); // John
```

## "var" variables can be declared below their use

当函数开始的时候,就会处理 `var` 声明(脚本启动对应全局变量)。

Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
function sum(a) {

let currentSum = a;

function f(b) {
currentSum += b;
return f;
}

f.toString = function() {
return currentSum;
};

return f;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
function sum(a){
// Your code goes here.

}

/*
sum(1)(2) == 3; // 1 + 2
sum(1)(2)(3) == 6; // 1 + 2 + 3
sum(5)(-1)(2) == 6
sum(6)(-1)(-2)(-3) == 0
sum(0)(1)(2)(3)(4)(5) == 15
*/
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
describe("sum", function(){

it("sum(1)(2) == 3", function(){
assert.equal(3, sum(1)(2));
});

it("sum(5)(-1)(2) == 6", function(){
assert.equal(6, sum(5)(-1)(2));
});

it("sum(6)(-1)(-2)(-3) == 0", function(){
assert.equal(0, sum(6)(-1)(-2)(-3));
});

it("sum(0)(1)(2)(3)(4)(5) == 15", function(){
assert.equal(15, sum(0)(1)(2)(3)(4)(5));
});
});

Original file line number Diff line number Diff line change
@@ -7,8 +7,8 @@ describe("throttle(f, 1000)", function() {
}

before(function() {
f1000 = throttle(f, 1000);
this.clock = sinon.useFakeTimers();
f1000 = throttle(f, 1000);
});

it("the first call runs now", function() {
Original file line number Diff line number Diff line change
@@ -4,17 +4,21 @@ importance: 5

# 节流装饰者

创建一个“节流”装饰者 `throttle(f, ms)` —— 返回一个包装器,最多每隔 `1ms` 将调用传递给 `f` 一次。那些属于“冷却”期的调用将被忽略
创建一个“节流”装饰者 `throttle(f, ms)` —— 返回一个包装器。

**与 `debounce` 的区别 —— 如果被忽略的调用是冷却期间的最后一次,那么它会在延时结束时执行。**
When it's called multiple times, it passes the call to `f` at maximum once per `ms` milliseconds.

The difference with debounce is that it's completely different decorator:
- `debounce` runs the function once after the "cooldown" period. Good for processing the final result.
- `throttle` runs it not more often than given `ms` time. Good for regular updates that shouldn't be very often.

让我们看看现实生活中的应用程序,以便更好地理解这个需求,并了解它的来源。

**例如,我们想要跟踪鼠标移动。**

在浏览器中,我们可以设置一个函数,使其在每次鼠标移动时运行,并获取鼠标移动时的指针位置。在使用鼠标的过程中,此函数通常会执行地非常频繁,大概每秒 100 次(每 10 毫秒)。

**当鼠标指针移动时,我们想要更新网页上的某些信息。**
**我们想要在鼠标指针移动时,更新网页上的某些信息。**

……但是更新函数 `update()` 太重了,无法在每个微小移动上都执行。高于每 100ms 更新一次的更新频次也没有意义。

10 changes: 6 additions & 4 deletions 1-js/06-advanced-functions/09-call-apply-decorators/article.md
Original file line number Diff line number Diff line change
@@ -209,7 +209,7 @@ alert( worker.slow(2) ); // 工作正常,没有调用原始函数(使用的
2. 因此,当 `worker.slow(2)` 执行时,包装器将 `2` 作为参数,并且 `this=worker`(它是点符号 `.` 之前的对象)。
3. 在包装器内部,假设结果尚未缓存,`func.call(this, x)` 将当前的 `this`(`=worker`)和当前的参数(`=2`)传递给原始方法。

## 使用 "func.apply" 来传递多参数
## 传递多个参数

现在让我们把 `cachingDecorator` 写得更加通用。到现在为止,它只能用于单参数函数。

@@ -236,7 +236,7 @@ worker.slow = cachingDecorator(worker.slow);

对于许多实际应用,第三种方式就足够了,所以我们就用这个吧。

当然,我们需要将 `func.call(this, x)` 替换成 `func.call(this, ...arguments)`,以将所有参数传递给包装的函数调用,而不仅仅是只传递第一个参数。
Also we need to pass not just `x`, but all arguments in `func.call`. Let's recall that in a `function()` we can get a pseudo-array of its arguments as `arguments`, so `func.call(this, x)` should be replaced with `func.call(this, ...arguments)`.

这是一个更强大的 `cachingDecorator`:

@@ -284,6 +284,8 @@ alert( "Again " + worker.slow(3, 5) ); // same (cached)
- 在 `(*)` 行中它调用 `hash` 来从 `arguments` 创建一个单独的键。这里我们使用一个简单的“连接”函数,将参数 `(3, 5)` 转换为键 `"3,5"`。更复杂的情况可能需要其他哈希函数。
- 然后 `(**)` 行使用 `func.call(this, ...arguments)` 将包装器获得的上下文和所有参数(不仅仅是第一个参数)传递给原始函数。

## func.apply

我们可以使用 `func.apply(this, arguments)` 代替 `func.call(this, ...arguments)`。

内建方法 [func.apply](mdn:js/Function/apply) 的语法是:
@@ -308,9 +310,9 @@ func.apply(context, args); // 与使用 call 相同
- Spread 语法 `...` 允许将 **可迭代对象** `args` 作为列表传递给 `call`。
- `apply` 仅接受 **类数组对象** `args`。

因此,这些调用可以相互补充。当我们期望可迭代对象时,使用 `call`,当我们期望类数组对象时,使用 `apply`。
因此,当我们期望可迭代对象时,使用 `call`,当我们期望类数组对象时,使用 `apply`。

对于即可迭代又是类数组的对象,例如一个真正的数组,从技术上讲我们使用 `call` 或 `apply` 都行,但是 `apply` 可能会更快,因为大多数 JavaScript 引擎在内部对其进行了优化。
对于即可迭代又是类数组的对象,例如一个真正的数组,我们使用 `call` 或 `apply` 均可,但是 `apply` 可能会更快,因为大多数 JavaScript 引擎在内部对其进行了优化。

将所有参数连同上下文一起传递给另一个函数被称为“呼叫转移(call forwarding)”。

2 changes: 1 addition & 1 deletion 1-js/07-object-properties/02-property-accessors/article.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@

# 属性的 getter 和 setter

有两种类型的属性
有两种类型的对象属性

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

5 changes: 2 additions & 3 deletions 1-js/08-prototypes/04-prototype-methods/article.md
Original file line number Diff line number Diff line change
@@ -57,7 +57,6 @@ alert(rabbit.jumps); // true
我们可以使用 `Object.create` 来实现比复制 `for..in` 循环中的属性更强大的对象克隆方式:

```js
// 完全相同的对象 obj 的浅拷贝
let clone = Object.create(Object.getPrototypeOf(obj), Object.getOwnPropertyDescriptors(obj));
```

@@ -116,7 +115,7 @@ alert(obj[key]); // [object Object],并不是 "some value"!

我们怎么避免这样的问题呢?

首先,我们可以改用 `Map`,那么一切都迎刃而解
首先,我们可以改用 `Map` 来代替普通对象进行存储,这样一切都迎刃而解

但是 `Object` 在这里同样可以运行得很好,因为 JavaScript 语言的制造者很早就注意到了这个问题。

@@ -128,7 +127,7 @@ alert(obj[key]); // [object Object],并不是 "some value"!

就像在本部分教程的开头所说的那样:`__proto__` 是一种访问 `[[Prototype]]` 的方式,而不是 `[[prototype]]` 本身。

现在,我们想要将一个对象用作关联数组,我们可以使用一些小技巧:
现在,我们想要将一个对象用作关联数组,并且摆脱此类问题,我们可以使用一些小技巧:

```js run
*!*