diff --git a/1-js/05-data-types/02-number/article.md b/1-js/05-data-types/02-number/article.md index a1056bd969..becdd2e6ea 100644 --- a/1-js/05-data-types/02-number/article.md +++ b/1-js/05-data-types/02-number/article.md @@ -148,7 +148,7 @@ alert( num.toString(2) ); // 11111111 1. 乘除法 - 例如,要将数字舍入到小数点后两位,我们可以将数字乘以 `100`,调用舍入函数,然后再将其除回。 + 例如,要将数字舍入到小数点后两位,我们可以将数字乘以 `100`(或更大的 10 的整数次幂),调用舍入函数,然后再将其除回。 ```js run let num = 1.23456; diff --git a/1-js/05-data-types/10-destructuring-assignment/article.md b/1-js/05-data-types/10-destructuring-assignment/article.md index d68856264a..73c183171c 100644 --- a/1-js/05-data-types/10-destructuring-assignment/article.md +++ b/1-js/05-data-types/10-destructuring-assignment/article.md @@ -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. + + ### 剩余的 '...' 如果我们不只是要获得第一个值,还要将后续的所有元素都收集起来 — 我们可以使用三个点 `"..."` 来再加一个参数来接收“剩余的”元素: diff --git a/1-js/05-data-types/11-date/6-get-seconds-today/solution.md b/1-js/05-data-types/11-date/6-get-seconds-today/solution.md index 58fec592b4..f830c7d12b 100644 --- a/1-js/05-data-types/11-date/6-get-seconds-today/solution.md +++ b/1-js/05-data-types/11-date/6-get-seconds-today/solution.md @@ -23,4 +23,6 @@ function getSecondsToday() { let d = new Date(); return d.getHours() * 3600 + d.getMinutes() * 60 + d.getSeconds(); }; + +alert( getSecondsToday() ); ``` diff --git a/1-js/06-advanced-functions/02-rest-parameters-spread/article.md b/1-js/06-advanced-functions/02-rest-parameters-spread/article.md index 31c4b14852..f09e3f5927 100644 --- a/1-js/06-advanced-functions/02-rest-parameters-spread/article.md +++ b/1-js/06-advanced-functions/02-rest-parameters-spread/article.md @@ -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]; diff --git a/1-js/06-advanced-functions/03-closure/9-sort-by-field/_js.view/solution.js b/1-js/06-advanced-functions/03-closure/9-sort-by-field/_js.view/solution.js new file mode 100644 index 0000000000..8a71c869d9 --- /dev/null +++ b/1-js/06-advanced-functions/03-closure/9-sort-by-field/_js.view/solution.js @@ -0,0 +1,3 @@ +function byField(fieldName){ + return (a, b) => a[fieldName] > b[fieldName] ? 1 : -1; +} diff --git a/1-js/06-advanced-functions/03-closure/9-sort-by-field/_js.view/source.js b/1-js/06-advanced-functions/03-closure/9-sort-by-field/_js.view/source.js new file mode 100644 index 0000000000..23b4338340 --- /dev/null +++ b/1-js/06-advanced-functions/03-closure/9-sort-by-field/_js.view/source.js @@ -0,0 +1,5 @@ +function byField(fieldName){ + + // Your code goes here. + +} diff --git a/1-js/06-advanced-functions/03-closure/9-sort-by-field/_js.view/test.js b/1-js/06-advanced-functions/03-closure/9-sort-by-field/_js.view/test.js new file mode 100644 index 0000000000..e3c335e03a --- /dev/null +++ b/1-js/06-advanced-functions/03-closure/9-sort-by-field/_js.view/test.js @@ -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); + }); + +}); diff --git a/1-js/06-advanced-functions/03-closure/9-sort-by-field/solution.md b/1-js/06-advanced-functions/03-closure/9-sort-by-field/solution.md index bd57085eaf..8b13789179 100644 --- a/1-js/06-advanced-functions/03-closure/9-sort-by-field/solution.md +++ b/1-js/06-advanced-functions/03-closure/9-sort-by-field/solution.md @@ -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 -``` - diff --git a/1-js/06-advanced-functions/04-var/article.md b/1-js/06-advanced-functions/04-var/article.md index 0be262b98a..bdc96ef265 100644 --- a/1-js/06-advanced-functions/04-var/article.md +++ b/1-js/06-advanced-functions/04-var/article.md @@ -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` 声明(脚本启动对应全局变量)。 diff --git a/1-js/06-advanced-functions/06-function-object/5-sum-many-brackets/_js.view/solution.js b/1-js/06-advanced-functions/06-function-object/5-sum-many-brackets/_js.view/solution.js new file mode 100644 index 0000000000..c7d7d734ea --- /dev/null +++ b/1-js/06-advanced-functions/06-function-object/5-sum-many-brackets/_js.view/solution.js @@ -0,0 +1,15 @@ +function sum(a) { + + let currentSum = a; + + function f(b) { + currentSum += b; + return f; + } + + f.toString = function() { + return currentSum; + }; + + return f; +} diff --git a/1-js/06-advanced-functions/06-function-object/5-sum-many-brackets/_js.view/source.js b/1-js/06-advanced-functions/06-function-object/5-sum-many-brackets/_js.view/source.js new file mode 100644 index 0000000000..f10dca5dc8 --- /dev/null +++ b/1-js/06-advanced-functions/06-function-object/5-sum-many-brackets/_js.view/source.js @@ -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 +*/ diff --git a/1-js/06-advanced-functions/06-function-object/5-sum-many-brackets/_js.view/test.js b/1-js/06-advanced-functions/06-function-object/5-sum-many-brackets/_js.view/test.js new file mode 100644 index 0000000000..ed567d3306 --- /dev/null +++ b/1-js/06-advanced-functions/06-function-object/5-sum-many-brackets/_js.view/test.js @@ -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)); + }); +}); + diff --git a/1-js/06-advanced-functions/09-call-apply-decorators/04-throttle/_js.view/test.js b/1-js/06-advanced-functions/09-call-apply-decorators/04-throttle/_js.view/test.js index d2cf8e1519..e671438f6f 100644 --- a/1-js/06-advanced-functions/09-call-apply-decorators/04-throttle/_js.view/test.js +++ b/1-js/06-advanced-functions/09-call-apply-decorators/04-throttle/_js.view/test.js @@ -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() { diff --git a/1-js/06-advanced-functions/09-call-apply-decorators/04-throttle/task.md b/1-js/06-advanced-functions/09-call-apply-decorators/04-throttle/task.md index ba1e5b61fd..0e2ae4001b 100644 --- a/1-js/06-advanced-functions/09-call-apply-decorators/04-throttle/task.md +++ b/1-js/06-advanced-functions/09-call-apply-decorators/04-throttle/task.md @@ -4,9 +4,13 @@ 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. 让我们看看现实生活中的应用程序,以便更好地理解这个需求,并了解它的来源。 @@ -14,7 +18,7 @@ importance: 5 在浏览器中,我们可以设置一个函数,使其在每次鼠标移动时运行,并获取鼠标移动时的指针位置。在使用鼠标的过程中,此函数通常会执行地非常频繁,大概每秒 100 次(每 10 毫秒)。 -**当鼠标指针移动时,我们想要更新网页上的某些信息。** +**我们想要在鼠标指针移动时,更新网页上的某些信息。** ……但是更新函数 `update()` 太重了,无法在每个微小移动上都执行。高于每 100ms 更新一次的更新频次也没有意义。 diff --git a/1-js/06-advanced-functions/09-call-apply-decorators/article.md b/1-js/06-advanced-functions/09-call-apply-decorators/article.md index 8388f82259..662dda98a5 100644 --- a/1-js/06-advanced-functions/09-call-apply-decorators/article.md +++ b/1-js/06-advanced-functions/09-call-apply-decorators/article.md @@ -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)”。 diff --git a/1-js/07-object-properties/02-property-accessors/article.md b/1-js/07-object-properties/02-property-accessors/article.md index 79f74fac66..7a9b450af8 100644 --- a/1-js/07-object-properties/02-property-accessors/article.md +++ b/1-js/07-object-properties/02-property-accessors/article.md @@ -1,7 +1,7 @@ # 属性的 getter 和 setter -有两种类型的属性。 +有两种类型的对象属性。 第一种是 **数据属性**。我们已经知道如何使用它们了。到目前为止,我们使用过的所有属性都是数据属性。 diff --git a/1-js/08-prototypes/04-prototype-methods/article.md b/1-js/08-prototypes/04-prototype-methods/article.md index b21172ee97..0193abebd7 100644 --- a/1-js/08-prototypes/04-prototype-methods/article.md +++ b/1-js/08-prototypes/04-prototype-methods/article.md @@ -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 *!*