diff --git a/1-js/04-object-basics/04-object-methods/2-check-syntax/solution.md b/1-js/04-object-basics/04-object-methods/2-check-syntax/solution.md index 34fd7af40a..6f5da77566 100644 --- a/1-js/04-object-basics/04-object-methods/2-check-syntax/solution.md +++ b/1-js/04-object-basics/04-object-methods/2-check-syntax/solution.md @@ -11,9 +11,9 @@ let user = { (user.go)() // error! ``` -大多数浏览器中的错误信息并不能说明出现了什么问题。 +大多数浏览器中的错误信息并不能说明是什么出现了问题。 -**出现此错误是因为在 `user = {...}` 之后遗漏了一个分号。** +**出现此错误是因为在 `user = {...}` 后面漏了一个分号。** JavaScript 不会在括号 `(user.go)()` 前自动插入分号,所以解析的代码如下: @@ -21,9 +21,9 @@ JavaScript 不会在括号 `(user.go)()` 前自动插入分号,所以解析的 let user = { go:... }(user.go)() ``` -那么,我们可以看到这样一个连接的表达式,在语法构成上,把对象 `{ go: ... }` 作为一个方法调用,并且传递的参数为 `(user.go)`。并且让 `let user`在同一行赋值,因此 `user` 没被定义(之前)就会出现错误 +然后我们还可以看到,这样的联合表达式在语法上是将对象 `{ go: ... }` 作为参数为 `(user.go)` 的函数。这发生在 `let user` 的同一行上,因此 `user` 对象是甚至还没有被定义,因此出现了错误。 -如果我们插入该分号,一切都会正常: +如果我们插入该分号,一切都变得正常: ```js run let user = { @@ -34,10 +34,4 @@ let user = { (user.go)() // John ``` -要注意的是 `(user.go)` 内的括号没有什么意义。通常用它们来设置操作的顺序,但在这里点 `.` 总是会先执行,所以并没有什么影响。分号是唯一重要的。 - - - - - - +要注意的是,`(user.go)` 外边这层括号在这没有任何作用。通常用它们来设置操作的顺序,但在这里点符号 `.` 总是会先执行,所以并没有什么影响。分号是唯一重要的。 diff --git a/1-js/04-object-basics/04-object-methods/3-why-this/solution.md b/1-js/04-object-basics/04-object-methods/3-why-this/solution.md index 0feaf16eae..b102c4c5c4 100644 --- a/1-js/04-object-basics/04-object-methods/3-why-this/solution.md +++ b/1-js/04-object-basics/04-object-methods/3-why-this/solution.md @@ -1,22 +1,21 @@ -这里是解释。 +这里是解析。 1. 它是一个常规的方法调用。 -2. 同样,括号没有改变执行的顺序,点总是首先执行。 +2. 同样,括号没有改变执行的顺序,点符号总是先执行。 3. 这里我们有一个更复杂的 `(expression).method()` 调用。这个调用就像被分成了两行(代码)一样: ```js no-beautify - f = obj.go; // calculate the expression - f(); // call what we have + f = obj.go; // 计算函数表达式 + f(); // 调用 ```    这里的 `f()` 是作为一个没有(设定)`this` 的函数执行的。 -4. 与 `(3)` 相类似,在点 `.` 的左边也有一个表达式。 +4. 与 `(3)` 相类似,在点符号 `.` 的左边也有一个表达式。 -要解释 `(3)` 和 `(4)` 的原因,我们需要回顾一下属性访问器(点或方括号)返回的值是引用类型的。 - -除了方法调用之外的任何操作(如赋值 `=` 或 `||` 等)把它变为了一个没有设定 `this` 信息的普通值。 +要解释 `(3)` 和 `(4)` 得到这种结果的原因,我们需要回顾一下属性访问器(点符号或方括号)返回的是引用类型的值。 +除了方法调用(如赋值 `=` 或 `||`)之外的任何操作,都会把它转换为一个不包含允许设置 `this` 信息的普通值。 diff --git a/1-js/04-object-basics/04-object-methods/3-why-this/task.md b/1-js/04-object-basics/04-object-methods/3-why-this/task.md index 3a59ecadc1..54365f7140 100644 --- a/1-js/04-object-basics/04-object-methods/3-why-this/task.md +++ b/1-js/04-object-basics/04-object-methods/3-why-this/task.md @@ -4,9 +4,9 @@ importance: 3 # 解释 "this" 的值 -在下面的代码中,我们试图连续调用 4 次 `user.go()` 方法。 +在下面的代码中,我们试图连续调用 `obj.go()` 方法 4 次。 -但是 `(1)` 和 `(2)` 次 `(3)` 和 `(4)` 调用结果不同,为什么呢? +但是前两次和后两次调用的结果不同,为什么呢? ```js run no-beautify let obj, method; diff --git a/1-js/04-object-basics/04-object-methods/4-object-property-this/solution.md b/1-js/04-object-basics/04-object-methods/4-object-property-this/solution.md index 1356a407ba..2885670e2c 100644 --- a/1-js/04-object-basics/04-object-methods/4-object-property-this/solution.md +++ b/1-js/04-object-basics/04-object-methods/4-object-property-this/solution.md @@ -14,13 +14,24 @@ let user = makeUser(); alert( user.ref.name ); // Error: Cannot read property 'name' of undefined ``` -这是因为设置的 `this` 的规则并没有找到对象字面量。 +这是因为设置 `this` 的规则不考虑对象定义。只有调用那一刻才重要。 -这里 `makeUser()` 中的 `this` 值是 `undefined`,因为它是被作为函数调用的,而不是方法调用。 +这里 `makeUser()` 中的 `this` 的值是 `undefined`,因为它是被作为函数调用的,而不是通过点符号被作为方法调用。 -对象字面量本身对于 `this` 没有影响。`this` 的值是整个函数,代码段和对象字面量对它没有影响。 +`this` 的值是对于整个函数的,代码段和对象字面量对它都没有影响。 -所以,`ref: this` 实际上取的是该函数当前的 `this`。 +所以 `ref: this` 实际上取的是当前函数的 `this`。 + +我们可以重写这个函数,并返回和上面相同的值为 `undefined` 的 `this`: + +```js run +function makeUser(){ + return this; // 这次这里没有对象字面量 +} + +alert( makeUser().name ); // Error: Cannot read property 'name' of undefined +``` +我们可以看到 `alert( makeUser().name )` 的结果和前面那个例子中 `alert( user.ref.name )` 的结果相同。 这里有个反例: @@ -41,6 +52,4 @@ let user = makeUser(); alert( user.ref().name ); // John ``` -现在正常了,因为 `user.ref()` 是一个方法。`this` 的值设置为点 `.` 之前的这个对象。 - - +现在正常了,因为 `user.ref()` 是一个方法。`this` 的值为点符号 `.` 前的这个对象。 diff --git a/1-js/04-object-basics/04-object-methods/4-object-property-this/task.md b/1-js/04-object-basics/04-object-methods/4-object-property-this/task.md index c990362c46..9f125c23e7 100644 --- a/1-js/04-object-basics/04-object-methods/4-object-property-this/task.md +++ b/1-js/04-object-basics/04-object-methods/4-object-property-this/task.md @@ -18,6 +18,5 @@ function makeUser() { let user = makeUser(); -alert( user.ref.name ); // What's the result? +alert( user.ref.name ); // 结果是什么? ``` - diff --git a/1-js/04-object-basics/04-object-methods/7-calculator/solution.md b/1-js/04-object-basics/04-object-methods/7-calculator/solution.md index 22c4bf187c..459997624e 100644 --- a/1-js/04-object-basics/04-object-methods/7-calculator/solution.md +++ b/1-js/04-object-basics/04-object-methods/7-calculator/solution.md @@ -1,6 +1,5 @@ - -```js run demo +```js run demo solution let calculator = { sum() { return this.a + this.b; @@ -20,4 +19,3 @@ calculator.read(); alert( calculator.sum() ); alert( calculator.mul() ); ``` - diff --git a/1-js/04-object-basics/04-object-methods/7-calculator/task.md b/1-js/04-object-basics/04-object-methods/7-calculator/task.md index ac10808a42..280259443d 100644 --- a/1-js/04-object-basics/04-object-methods/7-calculator/task.md +++ b/1-js/04-object-basics/04-object-methods/7-calculator/task.md @@ -2,17 +2,17 @@ importance: 5 --- -# 创建一个计算器(对象) +# 创建一个计算器 创建一个有三个方法的 `calculator` 对象: -- `read()` 提示输入两个值,将其保存为对象属性。 +- `read()` 提示输入两个值,并将其保存为对象属性。 - `sum()` 返回保存的值的和。 -- `mul()` 将保存的值相乘并返回其结果。 +- `mul()` 将保存的值相乘并返回计算结果。 ```js let calculator = { - // ... your code ... + // ……你的代码…… }; calculator.read(); @@ -21,4 +21,3 @@ alert( calculator.mul() ); ``` [demo] - diff --git a/1-js/04-object-basics/04-object-methods/8-chain-calls/solution.md b/1-js/04-object-basics/04-object-methods/8-chain-calls/solution.md index a19f7a77bd..1cc235b517 100644 --- a/1-js/04-object-basics/04-object-methods/8-chain-calls/solution.md +++ b/1-js/04-object-basics/04-object-methods/8-chain-calls/solution.md @@ -1,6 +1,6 @@ 解决方案就是在每次调用后返回这个对象本身。 -```js run +```js run demo let ladder = { step: 0, up() { diff --git a/1-js/04-object-basics/04-object-methods/8-chain-calls/task.md b/1-js/04-object-basics/04-object-methods/8-chain-calls/task.md index 2d37bf4eb5..189b7755f2 100644 --- a/1-js/04-object-basics/04-object-methods/8-chain-calls/task.md +++ b/1-js/04-object-basics/04-object-methods/8-chain-calls/task.md @@ -15,13 +15,13 @@ let ladder = { down() { this.step--; }, - showStep: function() { // shows the current step + showStep: function() { // 显示当前的 step alert( this.step ); } }; ``` -现在如果我们要依次执行几次调用,可以这样做: +现在,如果我们要按顺序执行几次调用,可以这样做: ```js ladder.up(); @@ -30,10 +30,10 @@ ladder.down(); ladder.showStep(); // 1 ``` -修改 `up` 和 `down` 的代码让调用可以链接,就像这样: +修改 `up`,`down` 和 `showStep` 的代码,让调用可以链接,就像这样: ```js ladder.up().up().down().showStep(); // 1 ``` -此种方法在 JavaScript 库中被广泛使用。 +这种方法在 JavaScript 库中被广泛使用。 diff --git a/1-js/04-object-basics/04-object-methods/article.md b/1-js/04-object-basics/04-object-methods/article.md index bb8ffd9660..d4faae8be9 100644 --- a/1-js/04-object-basics/04-object-methods/article.md +++ b/1-js/04-object-basics/04-object-methods/article.md @@ -1,4 +1,4 @@ -# 对象方法与,"this" +# 对象方法,"this" 通常创建对象来表示真实世界中的实体,如用户和订单等: @@ -94,9 +94,9 @@ let user = { ## 方法中的 "this" -在很多时候,对象方法需要访问对象中的存储的信息来完成其工作。 +通常,对象方法需要访问对象中存储的信息才能完成其工作。 -举个例子,`user.sayHi()` 中的代码可能需要用到 `user` 的 name 属性。 +例如,`user.sayHi()` 中的代码可能需要用到 `user` 的 name 属性。 **为了访问该对象,方法中可以使用 `this` 关键字。** @@ -111,6 +111,7 @@ let user = { sayHi() { *!* + // "this" 指的是“当前的对象” alert(this.name); */!* } @@ -138,9 +139,9 @@ let user = { }; ``` -但这样的代码是不可靠的。如果我们将 `user` 复制给另一个变量。例如 `admin = user`,并赋另外的值给 `user`,那么它将访问到错误的对象。 +……但这样的代码是不可靠的。如果我们决定将 `user` 复制给另一个变量,例如 `admin = user`,并赋另外的值给 `user`,那么它将访问到错误的对象。 -如下所示: +下面这个示例证实了这一点: ```js run let user = { @@ -157,18 +158,18 @@ let user = { let admin = user; -user = null; // 覆盖让其更易懂 +user = null; // 重写让其更明显 -admin.sayHi(); // 噢哟!在 sayHi() 使用了旧的变量名。错误! +admin.sayHi(); // 噢哟!在 sayHi() 使用了旧的 name 属性!报错! ``` -如果在 `alert` 中以 `this.name` 替换 `user.name`,那么代码就会正常运行。 +如果我们在 `alert` 中以 `this.name` 替换 `user.name`,那么代码就会正常运行。 -## “this” 不受限制 +## "this" 不受限制 -在 JavaScript 中,"this" 关键字与大多数其他编程语言中的不同。首先,它可以用于任何函数。 +在 JavaScript 中,`this` 关键字与其他大多数编程语言中的不同。JavaScript 中的 `this` 可以用于任何函数。 -这样的代码没有语法错误: +下面这样的代码没有语法错误: ```js function sayHi() { @@ -176,9 +177,9 @@ function sayHi() { } ``` -`this` 是在运行时求值的。它可以是任何值。 +`this` 的值是在代码运行时计算出来的,它取决于代码上下文。 -例如,从不同的对象中调用同一个函数可能会有不同的 "this" 值: +例如,这里相同的函数被分配给两个不同的对象,在调用中有着不同的 "this" 值: ```js run let user = { name: "John" }; @@ -189,20 +190,23 @@ function sayHi() { } *!* -// 在两个对象中使用的是相同的函数 +// 在两个对象中使用相同的函数 user.f = sayHi; admin.f = sayHi; */!* -// 它们调用时有不同的 this 值。 -// 函数内部的 "this" 是点之前的这个对象。 -user.f(); // John (this == user) -admin.f(); // Admin (this == admin) +// 这两个调用有不同的 this 值 +// 函数内部的 "this" 是“点符号前面”的那个对象 +user.f(); // John(this == user) +admin.f(); // Admin(this == admin) -admin['f'](); // Admin(使用点或方括号语法来访问这个方法,都没有关系。) +admin['f'](); // Admin(使用点符号或方括号语法来访问这个方法,都没有关系。) ``` -实际上,我们可以在没有任何对象的情况下调用函数: +这个规则很简单:如果 `obj.f()` 被调用了,则 `this` 在 `f` 函数调用期间是 `obj`。所以在上面的例子中 this 先是 `user`,之后是 `admin`。 + +````smart header="在没有对象的情况下调用:`this == undefined`" +我们甚至可以在没有对象的情况下调用函数: ```js run function sayHi() { @@ -212,31 +216,32 @@ function sayHi() { sayHi(); // undefined ``` -在这种情况下,严格模式下的 `this` 值为 `undefined`。如果我们尝试访问 `this.name`,将会出现错误。 +在这种情况下,严格模式下的 `this` 值为 `undefined`。如果我们尝试访问 `this.name`,将会报错。 -在非严格模式(没有使用 `use strict`)的情况下,`this` 将会是**全局对象**(浏览器中的 `window`,我们稍后会进行讨论)。`"use strict"` 可以修复这个历史行为。 +在非严格模式的情况下,`this` 将会是 **全局对象**(浏览器中的 `window`,我们稍后会在 [](info:global-object) 一章中学习它)。这是一个历史行为,`"use strict"` 已经将其修复了。 -请注意,通常在没有对象的情况下使用 `this` 的函数调用是不常见的,会(导致)编程错误。如果函数中有 `this`,那么通常意味着它是在对象上下文环境中被调用的。 +通常这种调用是程序出错了。如果在一个函数内部有 `this`,那么通常意味着它是在对象上下文环境中被调用的。 +```` -```smart header="The consequences of unbound `this`" -如果你来自其他的编程语言,那么你可能熟悉『绑定 `this`』的概念。在对象定义的方法中,`this` 总是指向该对象。 +```smart header="解除 `this` 绑定的后果" +如果你经常使用其他的编程语言,那么你可能已经习惯了“绑定 `this`”的概念,即在对象中定义的方法总是有指向该对象的 `this`。 -在 JavaScript 中,`this` 是『自由』的,它的值是在调用时进行求值的,它的值并不取决于方法声明的位置,而是(取决)于在『点之前』的是什么对象。 +在 JavaScript 中,`this` 是“自由”的,它的值是在调用时计算出来的,它的值并不取决于方法声明的位置,而是取决于在“点符号前”的是什么对象。 -在运行时对 `this` 求值的这个想法有其优缺点。一方面,函数可以被重用于不同的对象。另一方面,更大的灵活性给错误留下了余地。 +在运行时对 `this` 求值的这个概念既有优点也有缺点。一方面,函数可以被重用于不同的对象。另一方面,更大的灵活性造成了更大的出错的可能。 -这里我们的立场并不是要评判编程语言的这个想法的好坏,而是要了解怎样使用它,如何趋利避害。 +这里我们的立场并不是要评判编程语言的这个设计是好是坏。而是要了解怎样使用它,如何趋利避害。 ``` ## 内部:引用类型 -```warn header="In-depth language feature" -本文介绍一个进阶的主题,来更好地理解一些特殊情况。 +```warn header="高阶语言特性" +这一小节介绍了一个进阶主题,来更好地理解一些特殊情况。 -如果你想学得更快,这部分你可以跳过或过后来读。 +如果你想学得更快,这部分你可以跳过或者过后来读。 ``` -『复杂』的方法调用可能会失去 `this`,比如: +“复杂”的方法调用可能会失去 `this`,例如: ```js run let user = { @@ -245,40 +250,40 @@ let user = { bye() { alert("Bye"); } }; -user.hi(); // John (the simple call works) +user.hi(); // John(简单的调用工作正常) *!* -// 现在我们要判断 name 属性,来决定调用 user.hi 或是 user.bye。 -(user.name == "John" ? user.hi : user.bye)(); // Error! +// 现在我们要根据 name 来决定调用 user.hi 还是 user.bye。 +(user.name == "John" ? user.hi : user.bye)(); // 报错! */!* ``` -最后一行中有一个三元运算符,它要判断是 `user.hi` 或 `user.bye`。在这种情况下,结果会是 `user.hi`。 +最后一行中有一个三元运算符,用来决定调用 `user.hi` 还是 `user.bye`。在这种情况下,结果会是 `user.hi`。 -该方法立即被括号 `()` 调用。但它无效。 +该方法立即被括号 `()` 调用。但它不能正常工作! 你可以看到该调用导致了错误,因为调用中的 `"this"` 为 `undefined`。 -这样是正确的(对象点方法): +这样能正常工作(对象点方法): ```js user.hi(); ``` -这样没有效果(对方法求值): +这样不行(对方法求值): ```js -(user.name == "John" ? user.hi : user.bye)(); // 错误! +(user.name == "John" ? user.hi : user.bye)(); // 报错! ``` -原因是什么?如果我们想了解为什么会这样,那么我们要深入理解 `obj.method()` 调用的原理。 +为什么?如果我们想了解为什么会这样,那么我们要深入理解 `obj.method()` 的调用原理。 仔细看,我们可能注意到 `obj.method()` 语句中有两个操作符。 -1. 首先,点 `'.'` 取得这个 `obj.method` 属性。 +1. 首先,点符号 `'.'` 取得这个 `obj.method` 属性。 2. 其后的括号 `()` 调用它。 -那么,`this` 是如何从第一部分传递到第二部分的呢? +那么,`this` 是怎样被从第一部分传递到第二部分的呢? -如果把这些操作分离开,那么 `this` 肯定会丢失: +如果把这些操作拆分开,那么 `this` 肯定会丢失: ```js run let user = { @@ -293,36 +298,38 @@ hi(); // 错误,因为 this 未定义 */!* ``` -这里 `hi = user.hi` 把函数赋值给变量,其后的最后一行是完全独立的,所以它没有 `this`。 +这里 `hi = user.hi` 把函数赋值给变量,其后的最后一行代码是完全独立的,所以它没有 `this`。 -**为了让 `user.hi()` 有效,JavaScript 用一个技巧 —— 这个 `'.'` 点返回的不是一个函数,而是一种特殊的[引用类型](https://tc39.github.io/ecma262/#sec-reference-specification-type)的值。** +**为了让 `user.hi()` 有效,JavaScript 用了一个技巧 —— 这个 `'.'` 点符号返回的不是一个函数,而是一种特殊的 [引用类型](https://tc39.github.io/ecma262/#sec-reference-specification-type) 的值。** -引用类型是一种『规范中有的类型』。我们不能明确地指定它,但是可以在语言内部使用。 +引用类型是一种“规范中有的类型”。我们不能明确地指定它,但它被用在编程语言的内部。 -引用类型的值是三点的结合 `(base, name, strict)`,如下: +引用类型的值是三部分的结合 `(base, name, strict)`,如下: - `base` 是对象。 -- `name` 是属性。 -- 当 `use strict` 生效,`strict` 为真。 +- `name` 是属性名。 +- 在严格模式 `use strict` 下,`strict` 为真。 -`user.hi` 属性访问的值不是函数,而是引用类型的值。在严格模式下,`user.hi` 是: +属性访问 `user.hi` 的结果不是函数,而是引用类型。在严格模式下的 `user.hi` 是: ```js // 引用类型值 (user, "hi", true) ``` -`()` 被引用类型调用时,将接收关于该对象及其方法的所有信息,并且设定正确的 `this` 值(这里等于 `user`)。 +括号 `()` 调用引用类型时,将接收关于该对象及其方法的所有信息,并且可以设定正确的 `this` 值(这里等于 `user`)。 + +引用类型是一种特殊的“中间”内部类型,用于将信息从点符号 `.` 传递到调用括号 `()`。 -`hi = user.hi` 赋值等其他的操作,将引用类型作为一个整体丢弃,只获取 `user.hi`(一个函数)的值进行传递。因此,进一步操作『失去』了 `this`(值)。 +像赋值 `hi = user.hi` 等其他的操作,将引用类型作为一个整体丢弃,只获取 `user.hi`(一个函数)的值进行传递。因此,任何进一步的操作都会“失去” `this`。 -所以如果直接使用点 `obj.method()` 或方括号语法 `obj[method]()`(它们在这里并无差别)调用函数,那么作为结果,`this` 值会以正确的方式进行传递。 +因此,结果是,只有使用点符号 `obj.method()` 或方括号语法 `obj[method]()`(它们在这里作用相同)调用函数时,`this` 的值才被正确传递(这里的例子也一样)。在本教程的后面,我们将学习解决此问题的各种方法,例如 [func.bind()](/bind#solution-2-bind)。 ## 箭头函数没有自己的 "this" -箭头函数有些特别:它们没有自己的 `this`。如果我们在这样的函数中引用 `this`,`this` 值取决于外部『正常的』函数。 +箭头函数有些特别:它们没有自己的 `this`。如果我们在这样的函数中引用 `this`,`this` 值取决于外部“正常的”函数。 -举个例子,这里的 `arrow()` 使用的 `this` 来自外部的 `user.sayHi()` 方法: +举个例子,这里的 `arrow()` 使用的 `this` 来自于外部的 `user.sayHi()` 方法: ```js run let user = { @@ -336,18 +343,18 @@ let user = { user.sayHi(); // Ilya ``` -这是箭头函数的一个特征,当我们并不想要一个独立的 `this` 值,反而想从外部上下文中获取时,它很有用。在后面的章节 中我们将更深入地介绍箭头函数。 +这是箭头函数的一个特性,当我们并不想要一个独立的 `this`,反而想从外部上下文中获取时,它很有用。在后面的 一章中,我们将深入介绍箭头函数。 ## 总结 -- 存储在对象中函数称之为『方法』。 -- 对象执行方法进行『操作』,比如 `object.doSomething()`。 -- 方法可以将该对象引用为 `this`。 +- 存储在对象属性中函数被称为“方法”。 +- 方法允许对象进行像 `object.doSomething()` 这样的“操作”。 +- 方法可以将对象引用为 `this`。 -`this` 的值是在运行时求值的。 -- 函数声明使用的 `this` 只有等到调用时才会有值。 -- 函数可以在对象之间进行共用。 -- 当函数使用『方法』语法 `object.method()` 调用时,调用过程中的 `this` 总是指向 `object`。 +`this` 的值是在程序运行时得到的。 +- 一个函数在声明时,可能就使用了 `this`,但是这个 `this` 只有在函数被调用时才会有值。 +- 可以在对象之间复制函数。 +- 以“方法”的语法调用函数时:`object.method()`,调用过程中的 `this` 值是 `object`。 -请注意箭头函数有些特别:它们没有 `this`。在箭头函数内部访问的都是来自外部的 `this` 值。 +请注意箭头函数有些特别:它们没有 `this`。在箭头函数内部访问到的 `this` 都是从外部获取的。