diff --git a/1-js/04-object-basics/01-object/article.md b/1-js/04-object-basics/01-object/article.md index 1b21c1047a..14b8a92bfd 100644 --- a/1-js/04-object-basics/01-object/article.md +++ b/1-js/04-object-basics/01-object/article.md @@ -1,12 +1,12 @@ # 对象 -正如我们从《引言:类型》那章中知道的那样,JavaScript 中有很多种类型。有六种原始类型,因为他们只包含一种东西(字符串,数值或者什么之类)。 -相反,对象用来存储键值对和更复杂的实体。在 JavaScript 中,对象深入到这门语言的方方面面。所以在我们深入理解这门语言之前,我们必须理解对象。 +正如我们从《引言:类型》那章中知道的那样,JavaScript 中有很七种类型。有六种原始类型,因为他们的值只包含一种东西(字符串,数值或者其他)。 +相反,对象用来存储键值对和更复杂的实体。在 JavaScript 中,对象深入到这门语言的方方面面。所以在我们深入理解这门语言之前,必须先理解对象。 对象可以通过花括号 `{…}` 和其中包含一些可选的**属性**来创建。属性是一个键值对,键是一个字符串(也叫做属性名),值可以是任何类型。 -我们可以把对象想象成存放文件的橱柜。文件按照他们的名字来排列。这样根据文件名我们就很容易找到,添加或删除一个文件了。 +我们可以把对象想象成存放文件的橱柜。文件按照他们的名字来排列。这样根据文件名我们就很容易找到、添加或删除一个文件了。 ![](object.png) @@ -23,7 +23,7 @@ let user = {}; // “字面量” 的语法 ## 文本和属性 -我们可以在创建的时候立马给对象一些属性,在 `{...}` 里面放置一些键值对。 +我们可以在创建的时候立即给对象一些属性,在 `{...}` 里面放置一些键值对。 ```js let user = { // 一个对象 @@ -69,13 +69,13 @@ delete user.age; ![user object 3](object-user-delete.png) -我们也可以用多字词语来作为属性名,但是他们必须被包在一起: +我们也可以用多字词语来作为属性名,但是他们必须加上引号: ```js let user = { name: "John", age: 30, - "likes birds": true // 两个单词被包在一起 + "likes birds": true // 多词属性名必须加引号 }; ``` @@ -93,7 +93,7 @@ let user = { ## 方括号 -对于多字词语表示的属性,点操作就不能用啦。 +对于多词属性,点操作就不能用了: ```js run // 语法错误 @@ -189,11 +189,9 @@ let bag = { }; ``` -方括号比点方法获取属性更强大。它允许属性名和变量,它写起来也笨重多了。 - -大部分时间里,当属性名是简答和确切的时候,用点方法。如果有一些复杂的操作,那么就用方括号。 - +方括号比点符号更强大。它允许任何属性名和变量,但写起来也更加麻烦。 +大部分时间里,当属性名是已知且简单的时候,用点方法。如果有一些复杂的操作,那么就用方括号。 ```smart header="保留字段可以用作属性名" 变量名不能用保留字段,像:"for", "let", "return" 等。 @@ -224,11 +222,12 @@ alert(obj.__proto__); // [object Object],这样不行 比如,访问者可能选择 "__proto__" 作为键,这个赋值的逻辑就失败了(像上面那样)。 -有一种让对象把 `__proto__` 作为属性的方法,我们随后会讲到,现在我们再来学习对象的更多知识。 +有一种让对象把 `__proto__` 作为属性的方法,在后面章节会讲到,现在我们先来学习对象的更多知识。 还有另外一种数据结构 [Map](info:map-set-weakmap-weakset),我们会在后面章节学到,它支持任意的键值。 + ```` -## 便利的对象属性名 +## 属性值简写 在实际应用中,我们通常用存在的变量当做属性名。 @@ -263,7 +262,7 @@ function makeUser(name, age) { } ``` -我们可以把简便方式和正常方式混用: +我们可以把简写方式和正常方式混用: ```js let user = { @@ -364,11 +363,11 @@ for(let key in user) { ### 像对象一样排序 -对象有顺序吗?换句话说,如果我们便利一个对象,我们会按我们赋值属性的顺序来获得属性吗?这靠谱吗? +对象有顺序吗?换句话说,如果我们遍历一个对象,我们会按照赋值属性的顺序来获得属性吗?这靠谱吗? -简短的回答是:”有特别的顺序“:整数值属性有顺序,其他是按照创建的顺序,细节如下: +简短的回答是:”有特别的顺序“:整数属性有顺序,其他是按照创建的顺序,细节如下: -我们考虑下下面电话号码的例子: +例如,让我们考虑一个带有电话号码的对象: ```js run let codes = { @@ -386,29 +385,29 @@ for(let code in codes) { */!* ``` -对象会给用户一些建议,如果我们是为德国用户使用的网站,我们可能想 `49` 来当做第一个。 +对象可用于向用户建议选项列表。如果我们的网站主要面向德国用户,可能想让 `49` 来当做第一个。 -然而如果我们执行代码,我们会看到完全不同的景象: +然而如果我们执行代码,会看到完全不同的景象: -- USA (1) goes first -- then Switzerland (41) and so on. +- USA (1) 在最前面 +- 然后是 Switzerland (41) 以及其它内容 -这些电话码升序排序,因为他们是数字,所以我们看到 `1, 41, 44, 49`。 +因为这些电话号码是整数,所以它们以升序来排列。所以我们看到的是 `1, 41, 44, 49`。 -````smart header="数字属性?那是啥?" -数字属性这里代表一个表示属性的字符串能不做改变的转换成整数。 +````smart header="整数属性?那是什么?" +这里的“整数属性”术语指的是一个字符串,可以在不改变的情况下对整数进行转换。 -所以,"49" 是一个数字属性名,因为我们把它转换成整数,再转换回来,它还是一样。但是 "+49" 和 "1.2" 就不行了: +所以,"49" 是一个整数属性名,因为我们把它转换成整数,再转换回来,它还是一样。但是 "+49" 和 "1.2" 就不行了: ```js run // Math.trunc 是内置的去除小数点的方法。 -alert( String(Math.trunc(Number("49"))) ); // "49",同样,数字属性 -alert( String(Math.trunc(Number("+49"))) ); // "49",不同于 "+49" ⇒ 不是数字属性 -alert( String(Math.trunc(Number("1.2"))) ); // "1",不同于 "1.2" ⇒ 不是数字属性 +alert( String(Math.trunc(Number("49"))) ); // "49",同样,整数属性 +alert( String(Math.trunc(Number("+49"))) ); // "49",不同于 "+49" ⇒ 不是整数属性 +alert( String(Math.trunc(Number("1.2"))) ); // "1",不同于 "1.2" ⇒ 不是整数属性 ``` ```` -...另外一边,如果属性值不是数字,那它按照创建时候的顺序排序: +...另外一边,如果属性名不是整数,那它们就按照创建时候的顺序来排序: ```js run let user = { @@ -418,14 +417,14 @@ let user = { user.age = 25; // 增加一个 *!* -// 非数字属性是按照创建的顺序来排的。 +// 非整数属性是按照创建的顺序来排列的。 */!* for (let prop in user) { alert( prop ); // name, surname, age } ``` -所以,这就解决了电话码的问题,我们把数字属性转换成非数字的,在前面增加一个 `"+"` 就行了。 +所以,这就解决了电话号码的问题,我们把整数属性转换成非整数的,在前面增加一个 `"+"` 就行了。 像这样: @@ -464,7 +463,7 @@ let phrase = message; 对象跟这个不一样。 -**变量不存对象本身,只是对象的“内存地址”,是对象的引用。** +**变量存储的不是对象本身,而是对象的“内存地址”,是对象的引用。** 下面是对象的存储结构图: @@ -508,7 +507,7 @@ admin.name = 'Pete'; // 改变 "admin" 的引用 alert(*!*user.name*/!*); // 'Pete', changes are seen from the "user" reference ``` -上面的例子展示了只存在一个对象,如果我们的抽屉有两把钥匙,如果一个钥匙去使用了抽屉,另外一个钥匙打开的时候就会看到改变了。 +上面的例子展示了只存在一个对象,就像我们的抽屉有两把钥匙,如果一个钥匙(`admin`)去使用了抽屉,稍后使用另外一个钥匙(`user`)打开的时候,就会看到有变化。 ### 比较引用 @@ -526,7 +525,7 @@ alert( a == b ); // true,两个变量指向同一个对象 alert( a === b ); // true ``` -如果是两个不同的属性,他们就不相等,甚至他们都是空的。 +如果是两个不同的属性,他们就不相等,即使都是空的。 ```js run let a = {}; @@ -535,11 +534,11 @@ let b = {}; // 两个独立的对象 alert( a == b ); // false ``` -如果比较两个对象 `obj1 > obj2` 或者用一个对象比较原始值 `obj == 5`,对象被转换成原始值。我们不久就会学习到对象的转化是如何实现的,但是事实上,上面的比较真的没用,要不就是你代码写错了。 +如果比较两个对象 `obj1 > obj2` 或者用一个对象比较原始值 `obj == 5`,对象被转换成原始值。我们不久就会学习到对象的转化是如何实现的,但是事实上,上面的比较真的极少用到,要不就是你代码写错了。 ### 常量对象 -一个被 `const` 修饰的对象**可以**修改。 +一个被 `const` 修饰的对象**可以**被修改。 例如: @@ -555,7 +554,7 @@ user.age = 25; // (*) alert(user.age); // 25 ``` -看起来好像 `(*)` 这行会报错,但是不是的,这根本没问题。这是因为 `const` 仅仅修饰 `user`。在这里 `user` 存的是一个对象的引用。引用的地址没有变,只是引用的对象被修改了。 +看起来好像 `(*)` 这行会报错,但是不是的,这完全没问题。这是因为 `const` 仅仅修饰 `user`。在这里 `user` 始终存储的都是同一个对象的引用。引用的地址没有变,只是引用的对象被修改了。 如果你想把 `user` 赋值给其他的什么,那就会报错了,例如: @@ -582,7 +581,7 @@ user = { 这也是可行的,但是有一点麻烦,因为JS并没有原生的方法支持这么做。实际上,我们很少这么做。复制引用很多时候是好用的。 -如果我们真的想这么做,我们需要创建一个新的对象并且遍历现有对象的属性并且在原始值的状态下复制给新的对象。 +如果我们真的想这么做,就需要创建一个新的对象,遍历现有对象的属性,在原始值的状态下复制给新的对象。 像这样: @@ -615,8 +614,8 @@ alert( user.name ); // 原对象属性值不变 Object.assign(dest[, src1, src2, src3...]) ``` -- 参数 `dest` 和 `src1, ..., srcN` 可以是对象。 -- 这个方法复制了的所有对象到 `dest`。换句话说,第二个参数里面的对象的所有属性都复制给了第一个参数对象,然后返回 `dest`。 +- 参数 `dest` 和 `src1, ..., srcN`(可以有很多个)是对象。 +- 这个方法复制了 `src1, ..., srcN` 的所有对象到 `dest`。换句话说,从第二个参数开始,所有对象的属性都复制给了第一个参数对象,然后返回 `dest`。 例如,我们可以用这个方法来把几个对象合并成一个: ```js @@ -638,7 +637,7 @@ Object.assign(user, permissions1, permissions2); ```js let user = { name: "John" }; -// 重新 name,增加 isAdmin +// 覆盖 name,增加 isAdmin Object.assign(user, { name: "Pete", isAdmin: true }); // 现在 user = { name: "Pete", isAdmin: true } @@ -657,7 +656,7 @@ let clone = Object.assign({}, user); */!* ``` -它复制了所有 `user` 对象的属性给了一个空对象,然后返回拷贝后的对象。事实上,这跟循环赋值一样,但是更短。 +它复制了 `user` 对象所有的属性给了一个空对象,然后返回拷贝后的对象。事实上,这跟循环赋值一样,但是更短。 直到现在,我们是假设所有的 `user` 属性都是原始值,但是如果对象属性指向对象呢? @@ -701,10 +700,10 @@ alert(clone.sizes.width); // 51,在这里查看属性的值 ## 总结 -对象是联合的数组加上一些特别的特性。 +对象是具有一些特殊特性的关联数组。 他们存储键值对: -- 属性的键必须是字符串或者符号(通常也是字符串)。 +- 属性的键必须是字符串或者符号(通常是字符串)。 - 值可以是任何类型。 我们可以用下面的方法获取属性: @@ -714,23 +713,23 @@ alert(clone.sizes.width); // 51,在这里查看属性的值 其他操作: - 删除属性:`delete obj.prop`。 - 检查属性是否存在:`"key" in obj`。 -- 枚举属性:`for(let key in obj)` 循环。 +- 遍历对象:`for(let key in obj)` 循环。 -对象根据引用来赋值或者复制。换句话说,对象中不存 "值",而是 "引用" (内存地址)。 +对象根据引用来赋值或者复制。换句话说,变量存的不是对象的"值",而是值的 "引用"(内存地址)。 所以复制变量或者传递变量到方法中只是复制了对象的引用。 所有的引用操作(像增加,删除属性)都作用于同一个对象。 -深拷贝的话我们用 `Object.assign` 或者 [_.cloneDeep(obj)](https://lodash.com/docs#cloneDeep)。 +深拷贝的话我们可以使用 `Object.assign` 或者 [_.cloneDeep(obj)](https://lodash.com/docs#cloneDeep)。 -我们在这里学到的叫做“基本对象” -- 对象。 +我们在这一章学习的叫做“基本对象” — 对象。 -还有其他一些对象。 +JavaScript 中还有很多其他类型的对象: -- `Array` 存储队列数组。 +- `Array` 存储有序数据集合。 - `Date` 存储时间日期。 - `Error` 存储错误信息 - ...等等。 -他们有一些特别的特性,我们将在后面学习到。有时候大家说“数组类型”,“时间类型”,他们都属于对象类型的一种,都对对象类型做了一些延伸。 +他们有一些特别的特性,我们将在后面学习到。有时候大家说“数组类型”,“时间类型”,他们都属于对象类型的一种,都以不同的方式对对象类型做了一些扩展。 -对象在 JavaScript 中是很强大的,我们仅仅抓住庞然大物的表面一点谈了谈。我们将在后面的章节更进一步学习。 +对象在 JavaScript 中是很强大的,这里我们只接触了冰山一角。我们将在后面的章节更进一步学习。