From ae9d8b9c5df2f5059c84145ee475f9f2d4da8261 Mon Sep 17 00:00:00 2001 From: familyboat <2015301110129@whu.edu.cn> Date: Wed, 23 Oct 2024 11:34:27 +0800 Subject: [PATCH 01/16] =?UTF-8?q?=E5=BE=AE=E8=B0=83?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../javascript/inheritance_and_the_prototype_chain/index.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/files/zh-cn/web/javascript/inheritance_and_the_prototype_chain/index.md b/files/zh-cn/web/javascript/inheritance_and_the_prototype_chain/index.md index baec0f94082fd2..e7a2bc2badafa5 100644 --- a/files/zh-cn/web/javascript/inheritance_and_the_prototype_chain/index.md +++ b/files/zh-cn/web/javascript/inheritance_and_the_prototype_chain/index.md @@ -1,5 +1,5 @@ --- -title: 继承与原型链 +title: 继承和原型链 slug: Web/JavaScript/Inheritance_and_the_prototype_chain --- @@ -448,7 +448,7 @@ doSomething.prototype.prop: undefined doSomething.prototype.foo: bar ``` -## 使用不同的方法来创建对象和改变原型链 +## 不同的创建对象和改变原型链的方法 我们碰到过很多创建对象和改变其原型链的方法。我们将系统地总结不同的方法,并比较每种方法的优缺点。 @@ -740,7 +740,7 @@ Object.getPrototypeOf(g).hasOwnProperty("addVertex"); // true 注意:仅检查属性是否为 [`undefined`](/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/undefined) 是**不够的**。该属性很可能存在,但其值恰好设置为 `undefined`。 -## 结论 +## 总结 对于 Java 或 C++ 的开发者来说,JavaScript 可能有点令人困惑,因为它是完全动态、完全是在执行期间确定的,而且根本没有静态类型。一切都是对象(实例)或函数(构造函数),甚至函数本身也是 `Function` 构造函数的实例。即使是语法结构中的“类”也只是运行时的构造函数。 From 4a67d38f6a9a2ae5e4c23d9f7b169ce8755dac48 Mon Sep 17 00:00:00 2001 From: familyboat <2015301110129@whu.edu.cn> Date: Thu, 24 Oct 2024 23:24:36 +0800 Subject: [PATCH 02/16] =?UTF-8?q?=E5=90=8C=E6=AD=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../index.md | 271 +++++------------- 1 file changed, 70 insertions(+), 201 deletions(-) diff --git a/files/zh-cn/web/javascript/inheritance_and_the_prototype_chain/index.md b/files/zh-cn/web/javascript/inheritance_and_the_prototype_chain/index.md index e7a2bc2badafa5..9dd73c865fc82e 100644 --- a/files/zh-cn/web/javascript/inheritance_and_the_prototype_chain/index.md +++ b/files/zh-cn/web/javascript/inheritance_and_the_prototype_chain/index.md @@ -5,26 +5,24 @@ slug: Web/JavaScript/Inheritance_and_the_prototype_chain {{jsSidebar("Advanced")}} -对于使用过基于类的语言(如 Java 或 C++)的开发者来说,JavaScript 实在是有些令人困惑——JavaScript 是动态的且没有静态类型。 +在编程中,*继承*是指从父代向下传递特性给子代,这样新代码就能复用旧代码的特性以及在旧代码的特性上进行构建。JavaScript 使用[对象](/zh-CN/docs/Web/JavaScript/Data_structures#object)实现继承。每个对象都有一条链接到另一个称作*原型*的对象的内部链。该原型对象也有它自己的原型,直到将 `null` 作为原型的那个对象。根据定义,`null` 没有原型,它就像是这条*原型链*中最后的一环。在运行时修改原型链的任何成员、甚至是换掉原型都是可能的,所以 JavaScript 中不存在[静态分派](https://en.wikipedia.org/wiki/Static_dispatch)的概念。 -当谈到继承时,JavaScript 只有一种结构:对象。每个对象(object)都有一个私有属性指向另一个名为**原型**(prototype)的对象。原型对象也有一个自己的原型,层层向上直到一个对象的原型为 `null`。根据定义,`null` 没有原型,并作为这个**原型链**(prototype chain)中的最后一个环节。可以改变原型链中的任何成员,甚至可以在运行时换出原型,因此 JavaScript 中不存在[静态分派](https://en.wikipedia.org/wiki/Static_dispatch)的概念。 +JavaScript 对于使用过基于类的语言(如 Java 或 C++)的开发者来说,有些令人困惑——因为它是[动态的](/zh-CN/docs/Web/JavaScript/Data_structures#动态类型和弱类型)并且没有静态类型。尽管这个困惑通常认为是 JavaScript 的弱点之一,但是原型继承模型本身实际上比类式模型更强大。例如,在原型模型的基础上构建类式模型(即[类](/zh-CN/docs/Web/JavaScript/Reference/Classes)的实现方式)相当简单。 -尽管这种混杂通常被认为是 JavaScript 的弱点之一,但是原型继承模型本身实际上比类式模型更强大。例如,在原型模型的基础上构建类式模型(即[类](/zh-CN/docs/Web/JavaScript/Reference/Classes)的实现方式)相当简单。 - -尽管类现在被广泛采用并成为 JavaScript 中新的范式,但类并没有带来新的继承模式。虽然类为大部分原型的机制提供了抽象,但了解原型在底层是如何工作的仍然十分有用。 +尽管类现在被广泛使用并成为 JavaScript 中新的范式,但类并没有引入新的继承模式。尽管类抽象掉了大部分的原型机制,但理解原型在底层是如何工作的仍然十分有用。 ## 基于原型链的继承 ### 继承属性 -JavaScript 对象是动态的属性(指**其自有属性**)“包”。JavaScript 对象有一个指向一个原型对象的链。当试图访问一个对象的属性时,它不仅仅在该对象上搜寻,还会搜寻该对象的原型,以及原型的原型,依次层层向上搜索,直到找到一个名字匹配的属性或到达原型链的末尾。 +JavaScript 对象是动态的属性(指**其自有属性**)“包”。JavaScript 对象有一条指向原型对象的链。当试图访问对象的属性时,不仅在该对象上查找属性,还会在该对象的原型上查找属性,以及原型的原型,依次层层向上查找,直到找到一个名字匹配的属性或到达原型链的末尾。 > [!NOTE] -> 遵循 ECMAScript 标准,符号 `someObject.[[Prototype]]` 用于标识 `someObject` 的原型。内部插槽 `[[Prototype]]` 可以通过 {{jsxref("Object.getPrototypeOf()")}} 和 {{jsxref("Object.setPrototypeOf()")}} 函数来访问。这个等同于 JavaScript 的非标准但被许多 JavaScript 引擎实现的属性 [`__proto__`](/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Object/proto) 访问器。为在保持简洁的同时避免混淆,在我们的符号中会避免使用 `obj.__proto__`,而是使用 `obj.[[Prototype]]` 作为代替。其对应于 `Object.getPrototypeOf(obj)`。 +> 遵循 ECMAScript 标准,`someObject.[[Prototype]]` 符合用于标识 `someObject` 的原型。`[[Prototype]]` 内部插槽分别使用 {{jsxref("Object.getPrototypeOf()")}} 和 {{jsxref("Object.setPrototypeOf()")}} 函数访问和修改。其与非标准但许多 JavaScript 引擎实现的 [`__proto__`](/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Object/proto) 访问器等价。为了保持简洁和避免困惑,我们会避免使用 `obj.__proto__` 符号,而是使用 `obj.[[Prototype]]` 作为代替。其对应于 `Object.getPrototypeOf(obj)`。 > -> 它不应与函数的 `func.prototype` 属性混淆,后者指定在给定函数被用作构造函数时分配给所有对象*实例*的 `[[Prototype]]`。我们将在[后面的小节](#构造函数)中讨论构造函数的原型属性。 +> 它不应与函数的 `func.prototype` 属性混淆,后者赋值为指定函数用作构造函数时创建的所有对象*实例*的 `[[Prototype]]`。我们将在[后面的小节](#构造函数)中讨论构造函数的 `prototype` 属性。 -有几种可以指定对象的 `[[Prototype]]` 的方法,这些方法将在[后面的小节](#创建和改变原型链的不同方式)中列出。现在,我们将使用 [`__proto__` 语法](/zh-CN/docs/Web/JavaScript/Reference/Operators/Object_initializer#原型_setter)进行说明。值得注意的是,`{ __proto__: ... }` 语法与 `obj.__proto__` 访问器不同:前者是标准且未被弃用的。 +有几种可以指定对象的 `[[Prototype]]` 的方法,这些方法将在[后面的小节](#不同的创建对象和改变原型链的方法)中列出。现在,我们将使用 [`__proto__` 语法](/zh-CN/docs/Web/JavaScript/Reference/Operators/Object_initializer#原型_setter)进行说明。值得注意的是,`{ __proto__: ... }` 语法与 `obj.__proto__` 访问器不同:前者是标准且未废弃的。 在像 `{ a: 1, b: 2, __proto__: c }` 这样的对象字面量中,`c` 值(必须为 `null` 或另一个对象)将变成字面量所表示的对象的 `[[Prototype]]`,而其他键(如 `a` 和 `b`)将变成对象的*自有属性*。这种语法读起来非常自然,因为 `[[Prototype]]` 只是对象的“内部属性”。 @@ -44,8 +42,8 @@ const o = { // o.[[Prototype]] 具有属性 b 和 c。 // o.[[Prototype]].[[Prototype]] 是 Object.prototype(我们会在下文解释其含义)。 // 最后,o.[[Prototype]].[[Prototype]].[[Prototype]] 是 null。 -// 这是原型链的末尾,值为 null, -// 根据定义,其没有 [[Prototype]]。 +// 这是原型链的末尾, +// 因为根据定义,null 没有 [[Prototype]]。 // 因此,完整的原型链看起来像这样: // { a: 1, b: 2 } ---> { b: 3, c: 4 } ---> Object.prototype ---> null @@ -54,8 +52,8 @@ console.log(o.a); // 1 console.log(o.b); // 2 // o 上有自有属性“b”吗?有,且其值为 2。 -// 原型也有“b”属性,但其没有被访问。 -// 这被称为属性遮蔽(Property Shadowing) +// 原型也有属性“b”,但其没有被访问。 +// 这叫做属性遮蔽(Property Shadowing) console.log(o.c); // 4 // o 上有自有属性“c”吗?没有,检查其原型。 @@ -65,14 +63,14 @@ console.log(o.d); // undefined // o 上有自有属性“d”吗?没有,检查其原型。 // o.[[Prototype]] 上有自有属性“d”吗?没有,检查其原型。 // o.[[Prototype]].[[Prototype]] 是 Object.prototype 且 -// 其默认没有“d”属性,检查其原型。 +// 其默认没有属性“d”,检查其原型。 // o.[[Prototype]].[[Prototype]].[[Prototype]] 为 null,停止搜索, // 未找到该属性,返回 undefined。 ``` 给对象设置属性会创建自有属性。获取和设置行为规则的唯一例外是当它被 [getter 或 setter](/zh-CN/docs/Web/JavaScript/Guide/Working_with_objects#定义_getter_与_setter) 拦截时。 -同理,你可以创建更长的原型链,并在原型链上查找一个属性。 +同理,你可以创建更长的原型链,并在所有的原型上查找属性。 ```js const o = { @@ -95,9 +93,9 @@ console.log(o.d); // 5 ### 继承“方法” -JavaScript 并没有其他基于类的语言所定义的“[方法](/zh-CN/docs/Glossary/Method)”。在 JavaScript 中,任何函数都被可以添加到对象上作为其属性。函数的继承与其他属性的继承没有差别,包括上面的“属性遮蔽”(这种情况相当于其他语言的*方法重写*)。 +JavaScript 中定义的“[方法](/zh-CN/docs/Glossary/Method)”的形式和基于类的语言定义方法的形式不同。在 JavaScript 中,任何函数都能以属性的形式添加到对象上。继承函数与继承其他属性没有差别,包括上面的属性遮蔽(在函数继承中,称作*方法重写*)。 -当继承的函数被调用时,[`this`](/zh-CN/docs/Web/JavaScript/Reference/Operators/this) 值指向的是当前继承的对象,而不是拥有该函数属性的原型对象。 +当执行继承函数时,[`this`](/zh-CN/docs/Web/JavaScript/Reference/Operators/this) 值指向的是继承对象,而不是将该函数作为其自有属性的原型对象。 ```js const parent = { @@ -117,22 +115,21 @@ const child = { console.log(child.method()); // 3 // 调用 child.method 时,“this”指向了 child。 // 又因为 child 继承的是 parent 的方法, -// 首先在 child 上寻找“value”属性。但由于 child 本身 -// 没有名为“value”的自有属性,该属性会在 -// [[Prototype]] 上被找到,即 parent.value。 +// 首先在 child 上寻找“value”属性。 +// 然而,因为 child 没有名为“value”的自有属性, +// 该属性会在 [[Prototype]] 上被找到,即 parent.value。 -child.value = 4; // 在 child,将“value”属性赋值为 4。 +child.value = 4; // 将 child 上的“value”属性赋值为 4。 // 这会遮蔽 parent 上的“value”属性。 // child 对象现在看起来是这样的: // { value: 4, __proto__: { value: 2, method: [Function] } } console.log(child.method()); // 5 -// 因为 child 现在拥有“value”属性,“this.value”现在表示 -// child.value +// 因为 child 现在拥有“value”属性,“this.value”现在表示 child.value ``` ## 构造函数 -原型的强大之处在于,如果一组属性应该出现在每一个实例上,那我们就可以重用它们——尤其是对于方法。假设我们要创建多个盒子,其中每一个盒子都是一个对象,包含一个可以通过 `getValue` 函数访问的值。一个简单的实现可能是: +原型的强大之处在于,如果一组属性应该出现在每一个实例上,那我们就可以复用它们——尤其是对于方法。假设我们要创建多个盒子,其中每个盒子都是一个对象,包含一个可以通过 `getValue` 函数访问的值。一个简单的实现可能是: ```js-nolint const boxes = [ @@ -142,7 +139,7 @@ const boxes = [ ]; ``` -这是不够好的,因为每一个实例都有自己的,做相同事情的函数属性,这是冗余且不必要的。相反,我们可以将 `getValue` 移动到所有盒子的 `[[Prototype]]` 上: +这是不够好的,因为每个实例都有做相同事情的自有函数属性,这是冗余且不必要的。相反,我们可以将 `getValue` 移动到所有盒子的 `[[Prototype]]` 上: ```js const boxPrototype = { @@ -158,10 +155,10 @@ const boxes = [ ]; ``` -这样,所有盒子的 `getValue` 方法都会引用相同的函数,降低了内存使用率。但是,手动绑定每个对象创建的 `__proto__` 仍旧非常不方便。这时,我们就可以使用*构造函数*,它会自动为每个构造的对象设置 `[[Prototype]]`。构造函数是使用 [`new`](/zh-CN/docs/Web/JavaScript/Reference/Operators/new) 调用的函数。 +这样,所有盒子的 `getValue` 方法都会引用相同的函数,降低了内存使用率。但是,为每个对象创建手动绑定 `__proto__` 仍旧非常不方便。这时,我们就可以使用*构造函数*,它会自动为每个构造的对象设置 `[[Prototype]]`。构造函数是使用 [`new`](/zh-CN/docs/Web/JavaScript/Reference/Operators/new) 调用的函数。 ```js -// 一个构造函数 +// 构造函数 function Box(value) { this.value = value; } @@ -174,7 +171,7 @@ Box.prototype.getValue = function () { const boxes = [new Box(1), new Box(2), new Box(3)]; ``` -我们说 `new Box(1)` 是通过 `Box` 构造函数创建的一个*实例*。`Box.prototype` 与我们之前创建的 `boxPrototype` 并无太大区别——它只是一个普通的对象。通过构造函数创建的每一个实例都会自动将构造函数的 [`prototype`](/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Function/prototype) 属性作为其 `[[Prototype]]`。即,`Object.getPrototypeOf(new Box()) === Box.prototype`。`Constructor.prototype` 默认具有一个自有属性:[`constructor`](/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Object/constructor),它引用了构造函数本身。即,`Box.prototype.constructor === Box`。这允许我们在任何实例中访问原始构造函数。 +我们说 `new Box(1)` 是通过 `Box` 构造函数创建的一个*实例*。`Box.prototype` 与我们之前创建的 `boxPrototype` 并无太大区别——它就是一个普通的对象。通过构造函数创建的每一个实例都会自动将构造函数的 [`prototype`](/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Function/prototype) 属性作为其 `[[Prototype]]`。即,`Object.getPrototypeOf(new Box()) === Box.prototype`。`Constructor.prototype` 默认有一个自有属性:[`constructor`](/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Object/constructor),它引用了构造函数本身。即,`Box.prototype.constructor === Box`。这允许我们在任何实例中访问原始构造函数。 > [!NOTE] > 如果构造函数返回非原始值,则该值将成为 `new` 表达式的结果。在这种情况下,`[[Prototype]]` 可能无法正确绑定——但在实践中应该很少发生。 @@ -194,9 +191,9 @@ class Box { } ``` -类是构造函数的语法糖,这意味着你仍然可以修改 `Box.prototype` 来改变所有实例的行为。然而,由于类被设计为对底层原型机制的抽象,我们将在本教程中使用更轻量级的构造函数语法,以充分展示原型的工作原理。 +类是构造函数的语法糖,这意味着你仍然可以修改 `Box.prototype` 改变所有实例的行为。然而,由于类设计为对底层原型机制的抽象,我们将在本教程中使用更轻量级的构造函数语法,以充分展示原型的工作原理。 -因为 `Box.prototype` 引用了(作为所有实例的 `[[Prototype]]` 的)相同的对象,所以我们可以通过改变 `Box.prototype` 来改变所有实例的行为。 +因为 `Box.prototype` 引用的对象和所有实例的 `[[Prototype]]` 是同一个对象,所以我们可以通过改变 `Box.prototype` 改变所有实例的行为。 ```js function Box(value) { @@ -216,8 +213,8 @@ box.getValue(); // 2 有个推论是:_重新赋值_ `Constructor.prototype`(`Constructor.prototype = ...`)是一个不好的主意,原因有两点: -- 在重新赋值之前创建的实例的 `[[Prototype]]` 现在引用的是与重新赋值之后创建的实例的 `[[Prototype]]` 不同的对象——改变一个的 `[[Prototype]]` 不再改变另一个的 `[[Prototype]]`。 -- 除非你手动重新设置 `constructor` 属性,否则无法再通过 `instance.constructor` 追踪到构造函数,这可能会破坏用户期望的行为。一些内置操作也会读取 `constructor` 属性,如果没有设置,它们可能无法按预期工作。 +- 在重新赋值之前创建的实例的 `[[Prototype]]` 引用的对象现在与重新赋值之后创建的实例的 `[[Prototype]]` 引用的对象不同——改变一个的 `[[Prototype]]` 不会改变另一个的 `[[Prototype]]`。 +- 除非你手动重新设置 `constructor` 属性,否则无法再通过 `instance.constructor` 追踪到构造函数,这可能会打破用户期待。一些内置操作也会读取 `constructor` 属性,如果没有设置,它们可能无法按预期工作。 `Constructor.prototype` 仅在构造实例时有用。它与 `Constructor.[[Prototype]]` 无关,后者是构造函数的*自有*原型,即 `Function.prototype`。也就是说,`Object.getPrototypeOf(Constructor) === Function.prototype`。 @@ -226,8 +223,7 @@ box.getValue(); // 2 JavaScript 中的一些字面量语法会创建隐式设置 `[[Prototype]]` 的实例。例如: ```js -// 对象字面量(没有 `__proto__` 键)自动将 -// `Object.prototype` 作为它们的 `[[Prototype]]` +// 对象字面量(没有 `__proto__` 键)自动将 `Object.prototype` 作为它们的 `[[Prototype]]` const object = { a: 1 }; Object.getPrototypeOf(object) === Object.prototype; // true @@ -247,12 +243,12 @@ const array = new Array(1, 2, 3); const regexp = new RegExp("abc"); ``` -例如,像 [`map()`](/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Array/map) 这样的“数组方法”只是在 `Array.prototype` 上定义的方法,而它们又自动在所有数组实例上可用,就是因为这个原因。 +例如,像 [`map()`](/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Array/map) 这样的“数组方法”是仅在 `Array.prototype` 上定义的方法,而它们又自动在所有数组实例上可用,就是因为这个原因。 > [!WARNING] > 有一个常见的错误实践(misfeature):扩展 `Object.prototype` 或其它内置原型。这种不良特性例子是,定义 `Array.prototype.myMethod = function () {...}`,然后在所有数组实例上使用 `myMethod`。 > -> 这种错误实践被称为*猴子修补*(monkey patching)。使用猴子修补存在向前兼容的风险,因为如果语言在未来添加了此方法但具有不同的签名,你的代码将会出错。它已经导致了类似于 [SmooshGate](https://developer.chrome.google.cn/blog/smooshgate) 这样的事件,并且由于 JavaScript 致力于“不破坏 web”,因此这可能会对语言的发展造成极大的麻烦。 +> 这种错误实践被称为*猴子修补*(monkey patching)。使用猴子修补存在向前兼容的风险,因为如果语言在未来添加了此方法但具有不同的签名,你的代码将会出错。它已经导致了类似于 [SmooshGate](https://developer.chrome.google.cn/blog/smooshgate) 这样的事件,并且因为 JavaScript 致力于“不破坏 web”,所以这可能会对语言的发展造成极大的麻烦。 > > 扩展内置原型的**唯一**理由是向后移植新的 JavaScript 引擎的特性,比如 `Array.prototype.forEach`。 @@ -275,7 +271,7 @@ Map.prototype.get(1); ### 构建更长的继承链 -`Constructor.prototype` 属性将成为构造函数实例的 `[[Prototype]]`,包括 `Constructor.prototype` 自身的 `[[Prototype]]`。默认情况下,`Constructor.prototype` 是一个*普通对象*——即 `Object.getPrototypeOf(Constructor.prototype) === Object.prototype`。唯一的例外是 `Object.prototype` 本身,其 `[[Prototype]]` 是 `null`——即 `Object.getPrototypeOf(Object.prototype) === null`。因此,一个典型的构造函数将构建以下原型链: +`Constructor.prototype` 属性将成为构造函数的实例的 `[[Prototype]]`,包括 `Constructor.prototype` 的自有 `[[Prototype]]`。默认情况下,`Constructor.prototype` 是一个*普通对象*——即 `Object.getPrototypeOf(Constructor.prototype) === Object.prototype`。唯一的例外是 `Object.prototype` 本身,其 `[[Prototype]]` 是 `null`——即 `Object.getPrototypeOf(Object.prototype) === null`。因此,一个典型的构造函数将构建以下原型链: ```js function Constructor() {} @@ -284,7 +280,7 @@ const obj = new Constructor(); // obj ---> Constructor.prototype ---> Object.prototype ---> null ``` -要构建更长的原型链,我们可用通过 [`Object.setPrototypeOf()`](/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Object/setPrototypeOf) 函数设置 `Constructor.prototype` 的 `[[Prototype]]`。 +要构建更长的原型链,我们可以通过 [`Object.setPrototypeOf()`](/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Object/setPrototypeOf) 函数设置 `Constructor.prototype` 的 `[[Prototype]]`。 ```js function Base() {} @@ -307,14 +303,14 @@ const obj = new Derived(); // obj ---> Derived.prototype ---> Base.prototype ---> Object.prototype ---> null ``` -你可能还会看到一些使用 {{jsxref("Object.create()")}} 来构建继承链的旧代码。然而,因为这会重新为 `prototype` 属性赋值并删除 [`constructor`](/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Object/constructor) 属性,所以更容易出错,而且如果构造函数还没有创建任何实例,性能提升可能并不明显。 +你可能还会看到一些使用 {{jsxref("Object.create()")}} 构建继承链的旧代码。然而,因为这会重新为 `prototype` 属性赋值并删除 [`constructor`](/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Object/constructor) 属性,所以更容易出错,而且如果构造函数还没有创建任何实例,性能提升可能并不明显。 ```js example-bad function Base() {} function Derived() {} -// 将 `Derived.prototype` 重新赋值为 `Base.prototype`, -// 以作为其 `[[Prototype]]` 的新对象 -// 请不要这样做——使用 Object.setPrototypeOf 来修改它 +// 将 `Derived.prototype` 重新赋值为一个新对象, +// `Base.prototype` 作为新对象的 `[[Prototype]]` +// 请不要这样做——使用 Object.setPrototypeOf 修改它 Derived.prototype = Object.create(Base.prototype); ``` @@ -353,7 +349,7 @@ console.log(doSomethingFromArrowFunction.prototype); ``` > [!NOTE] -> Chrome 控制台使用 `[[Prototype]]` 来表示对象的原型,遵循规范的术语;Firefox 使用 ``。为了保持一致性,我们将使用 `[[Prototype]]`。 +> 遵循规范的术语,Chrome 控制台使用 `[[Prototype]]` 来表示对象的原型;Firefox 使用 ``。为了保持一致性,我们将使用 `[[Prototype]]`。 我们可以像下面这样,向 `doSomething()` 的原型添加属性。 @@ -381,7 +377,7 @@ console.log(doSomething.prototype); } ``` -我们现在可以使用 `new` 运算符来创建基于该原型的 `doSomething()` 的实例。要使用 new 运算符,只需像往常一样调用函数,只是要在前面加上 `new`。使用 `new` 运算符调用函数会返回一个函数的实例对象。然后可以在该对象上添加属性。 +我们现在可以使用 `new` 运算符创建基于该原型的 `doSomething()` 的实例。要使用 new 运算符,只需正常调用函数,只是要在前面加上 `new`。使用 `new` 运算符调用函数会返回该函数的实例对象。然后可以在该对象上添加属性。 尝试以下代码: @@ -389,7 +385,7 @@ console.log(doSomething.prototype); function doSomething() {} doSomething.prototype.foo = "bar"; // 向原型上添加一个属性 const doSomeInstancing = new doSomething(); -doSomeInstancing.prop = "some value"; // 向该对象添加一个属性 +doSomeInstancing.prop = "some value"; // 向对象添加一个属性 console.log(doSomeInstancing); ``` @@ -420,7 +416,7 @@ console.log(doSomeInstancing); 否则,如果 `doSomeInstancing.[[Prototype]]` 没有该属性,那么就会在 `doSomeInstancing.[[Prototype]].[[Prototype]]` 中查找该属性。默认情况下,任何函数的 `prototype` 属性的 `[[Prototype]]` 都是 `Object.prototype`。因此会在 `doSomeInstancing.[[Prototype]].[[Prototype]]`(也就是 `doSomething.prototype.[[Prototype]]`(也就是 `Object.prototype`))上查找该属性。 -如果在 `doSomeInstancing.[[Prototype]].[[Prototype]]` 中没有找到该属性,那么就会在 `doSomeInstancing.[[Prototype]].[[Prototype]].[[Prototype]]` 中查找该属性。但是,这里有一个问题:`doSomeInstancing.[[Prototype]].[[Prototype]].[[Prototype]]` 不存在,因为 `Object.prototype.[[Prototype]]` 是 `null`。然后,只有在查找完整个 `[[Prototype]]` 链之后,运行时才会断言该属性不存在,并得出该属性的值为 `undefined`。 +如果在 `doSomeInstancing.[[Prototype]].[[Prototype]]` 中没有找到该属性,那么就会在 `doSomeInstancing.[[Prototype]].[[Prototype]].[[Prototype]]` 中查找该属性。但是,这里有一个问题:`doSomeInstancing.[[Prototype]].[[Prototype]].[[Prototype]]` 不存在,因为 `Object.prototype.[[Prototype]]` 是 `null`。然后,只有在查找完整个 `[[Prototype]]` 的原型链之后,运行时才会断言该属性不存在,并得出该属性的值为 `undefined`。 让我们在控制台中输入更多的代码: @@ -457,12 +453,12 @@ doSomething.prototype.foo: bar ```js const o = { a: 1 }; // 新创建的对象 o 以 Object.prototype 作为它的 [[Prototype]] -// Object.prototype 的原型为 null。 +// Object.prototype 以 null 作为它的 [[Prototype]]。 // o ---> Object.prototype ---> null const b = ["yo", "whadup", "?"]; // 数组继承了 Array.prototype(具有 indexOf、forEach 等方法) -// 其原型链如下所示: +// 原型链如下所示: // b ---> Array.prototype ---> Object.prototype ---> null function f() { @@ -478,32 +474,7 @@ const p = { b: 2, __proto__: o }; // p ---> o ---> Object.prototype ---> null ``` - - - - - - - - - - - - -
- 在对象初始化器中使用 __proto__ 键的优缺点 -
优点 - 被所有的现代引擎所支持。将 __proto__ - 属性指向非对象的值只会被忽略,而非抛出异常。与 - {{jsxref("Object/proto", "Object.prototype.__proto__")}} setter 相反,对象字面量初始化器中的 __proto__ - 是标准化,被优化的。甚至可以比 {{jsxref("Object.create")}} 更高效。在创建对象时声明额外的自有属性比 {{jsxref("Object.create")}} - 更符合习惯。 -
缺点 - 不支持 IE10 及以下的版本。对于不了解其与 - {{jsxref("Object/proto", "Object.prototype.__proto__")}} - 访问器差异的人可能会将两者混淆。 -
+在[对象初始化器](/zh-CN/docs/Web/JavaScript/Reference/Operators/Object_initializer)中使用 `__proto__` 键时,将 `__proto__` 键指向非对象的值只会被忽略,而非抛出异常。与 [`Object.prototype.__proto__`](/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Object/proto) 的 setter 相反,对象字面量初始化器中的 `__proto__` 是标准化的、被优化的,甚至比 {{jsxref("Object.create")}} 更高效。在创建对象时声明额外的自有属性比 {{jsxref("Object.create")}} 更符合习惯。 ### 使用构造函数 @@ -522,29 +493,7 @@ const g = new Graph(); // 在执行 new Graph() 时,g.[[Prototype]] 是 Graph.prototype 的值。 ``` - - - - - - - - - - - - -
- 使用构造函数的优缺点 -
优点 - 所有引擎都支持——一直到 IE 5.5。此外,其速度很快、非常标准,且极易被 JIT 优化。 -
缺点 -
    -
  • 要使用这个方法,必须初始化该函数。在初始化过程中,构造函数可能会存储每一个对象都必须生成的唯一信息。这些唯一信息只会生成一次,可能会导致问题。
  • -
  • 构造函数的初始化过程可能会将不需要的方法放到对象上。
  • -
-

这两者在实践中通常都不是问题。

-
+自从非常早期的 JavaScript 开始,构造函数一直可用。因此,它是非常快速、非常标准,以及非常 JIT 优化过的。然而,它也很正确使用,因为以这种方式添加的方法默认是可枚举的,而这与类语法或内置方法的表现行为不一致。构造很长的继承链也容易出错,就像前面展示的那样。 ### 使用 Object.create() @@ -556,7 +505,7 @@ const a = { a: 1 }; const b = Object.create(a); // b ---> a ---> Object.prototype ---> null -console.log(b.a); // 1 (inherited) +console.log(b.a); // 1 (继承的) const c = Object.create(b); // c ---> b ---> a ---> Object.prototype ---> null @@ -567,81 +516,40 @@ console.log(d.hasOwnProperty); // undefined,因为 d 没有继承 Object.prototype ``` - - - - - - - - - - - - -
- {{jsxref("Object.create")}} 的优缺点 -
优点 - 被所有现代引擎所支持。允许在创建时直接设置对象的 - [[Prototype]],这允许运行时进一步优化对象。还允许使用 - Object.create(null) 创建没有原型的对象。 -
缺点 - 不支持 IE8 及以下版本。但是,由于微软已经停止了对运行 IE8 及以下版本的系统的扩展支持,这对大多数应用程序而言应该不是问题。此外,如果使用了第二个参数,慢对象的初始化可能会成为性能瓶颈,因为每个对象描述符属性都有自己单独的描述符对象。当处理上万个对象描述符时,这种延时可能会成为一个严重的问题。 -
+与对象初始化器中的 `__proto__` 键类似,`Object.create()` 允许在创建时直接设置对象的原型,这允许运行时进一步优化对象。还允许使用 `Object.create(null)` 创建原型为 `null` 的对象。`Object.create()` 的第二个参数能精确地指明新对象中每个属性地特性,而这会是一把双刃剑: + +- 它能在对象创建期间创建不可枚举地属性,而这用对象字面量做不到。 +- 它比对象字面量更加冗余以及更容易出错。 +- 它可能比对象字面量更慢,特别是创建很多属性时。 ### 使用类 ```js -class Polygon { +class Rectangle { constructor(height, width) { + this.name = "Rectangle"; this.height = height; this.width = width; } } -class Square extends Polygon { - constructor(sideLength) { - super(sideLength, sideLength); - } - - get area() { - return this.height * this.width; - } - - set sideLength(newLength) { - this.height = newLength; - this.width = newLength; +class FilledRectangle extends Rectangle { + constructor(height, width, color) { + super(height, width); + this.name = "Filled rectangle"; + this.color = color; } } -const square = new Square(2); -// square ---> Square.prototype ---> Polygon.prototype ---> Object.prototype ---> null +const filledRectangle = new FilledRectangle(5, 10, "blue"); +// filledRectangle ---> FilledRectangle.prototype ---> Rectangle.prototype ---> Object.prototype ---> null ``` - - - - - - - - - - - - -
- 类的优缺点 -
优点 - 被所有现代引擎所支持。非常高的可读性和可维护性。私有属性是原型继承中没有简单替代方案的特性。 -
缺点 - 类,尤其是带有私有属性的类,比传统的类的性能要差(尽管引擎实现者正在努力改进这一点)。不支持旧环境,通常需要转译器才能在生产中使用类。 -
+在定义复杂的继承结构式,类提供了非常高的可读性和可维护性。[私有属性](/zh-CN/docs/Web/JavaScript/Reference/Classes/Private_properties)是原型继承中没有简单替代方案的特性。然而,类比传统的构造函数的性能要差,以及不支持旧环境。 ### 使用 Object.setPrototypeOf() -虽然上面的所有方法都会在对象创建时设置原型链,但是 [`Object.setPrototypeOf()`](/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Object/setPrototypeOf) 允许修改现有对象的 `[[Prototype]]` 内部属性。 +虽然上面的所有方法都会在对象创建时设置原型链,但是 [`Object.setPrototypeOf()`](/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Object/setPrototypeOf) 允许修改现有对象的 `[[Prototype]]` 内部属性。它甚至能强制为 `Object.create(null)` 创建的无原型的对象设置原型,或者将原型设置为 `null` 移除对象的原型。 ```js const obj = { a: 1 }; @@ -650,32 +558,13 @@ Object.setPrototypeOf(obj, anotherObj); // obj ---> anotherObj ---> Object.prototype ---> null ``` - - - - - - - - - - - - -
- {{jsxref("Object.setPrototypeOf")}} 的优缺点 -
优点 - 被所有现代引擎所支持。允许动态地修改对象的原型,甚至可以强制为使用 - Object.create(null) 创建的无原型对象设置原型。 -
缺点 - 性能不佳。如果可以在创建对象时设置原型,则应避免此方法。许多引擎会优化原型,并在调用实例时会尝试提前猜测方法在内存中的位置;但是动态设置原型会破坏这些优化。它可能会导致某些引擎重新编译你的代码以进行反优化,以使其按照规范工作。不支持 IE8 及以下版本。 -
+然而,你应该尽可能在创建时设置原型,因为动态设置原型会破坏引擎已经为原型链做过的所有优化。它可能会造成一些引擎为了反优化而重新编译你的代码,以使其按照规范工作。 ### 使用 \_\_proto\_\_ 访问器 -所有对象都继承了 [`Object.prototype.__proto__`](/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Object/proto) 访问器,它可以用来设置现有对象的 `[[Prototype]]`(如果对象没有覆盖 `__proto__` 属性)。 +所有对象都继承了 [`Object.prototype.__proto__`](/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Object/proto) setter,它可以用来设置现有对象的 `[[Prototype]]`(如果对象没有覆盖 `__proto__` 属性)。 -> **警告:** `Object.prototype.__proto__` 访问器是**非标准**的,且已被弃用。你几乎总是应该使用 `Object.setPrototypeOf` 来代替。 +> **警告:** `Object.prototype.__proto__` 访问器是**非标准**的,且已废弃。你应该几乎总是使用 `Object.setPrototypeOf` 作为代替。 ```js const obj = {}; @@ -685,33 +574,13 @@ obj.__proto__.__proto__ = { fooProp: "foo val" }; console.log(obj.fooProp); console.log(obj.barProp); ``` - - - - - - - - - - - - - -
- 设置 {{jsxref("Object/proto","__proto__")}} 属性的优缺点 -
优点 - 被所有现代引擎所支持。将 {{jsxref("Object/proto","__proto__")}} - 设置为非对象的值只会被忽略,而非抛出异常。 -
缺点 - 性能不佳且已被弃用。许多引擎会优化原型,并在调用实例时会尝试提前猜测方法在内存中的位置;但是动态设置原型会破坏这些优化,甚至可能会导致某些引擎重新编译你的代码以进行反优化,以使其按照规范工作。不支持 IE10 及以下版本。{{jsxref("Object/proto","__proto__")}} 访问器是规范中可选的特性,因此可能无法在所有平台上使用。你几乎总是应该使用 {{jsxref("Object.setPrototypeOf")}} 代替。 -
+与 `Object.setPrototypeOf` 相比,将 `__proto__` 设置为非对象的值只会被忽略,而非抛出异常。它也只有稍微好一点的浏览器支持。然而,它是非标准的,并且已废弃。你应该几乎总是使用 `Object.setPrototypeOf` 作为代替。 ## 性能 -原型链上较深层的属性的查找时间可能会对性能产生负面影响,这在性能至关重要的代码中可能会非常明显。此外,尝试访问不存在的属性始终会遍历整个原型链。 +原型链较上层的属性的查找时间可能会对性能产生负面影响,这在性能至关重要的代码中可能会非常明显。此外,尝试访问不存在的属性始终会遍历整个原型链。 -此外,在遍历对象的属性时,原型链中的**每个**可枚举属性都将被枚举。要检查对象是否具有在其*自身*上定义的属性,而不是在其原型链上的某个地方,则有必要使用 [`hasOwnProperty`](/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Object/hasOwnProperty) 或 [`Object.hasOwn`](/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Object/hasOwn) 方法。除 `[[Prototype]]` 为 `null` 的对象外,所有对象都从 `Object.prototype` 继承 [`hasOwnProperty`](/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Object/hasOwnProperty)——除非它已经在原型链的更深处被覆盖。我们将使用上面的图示例代码来说明它,具体如下: +此外,在遍历对象的属性时,原型链中的**每个**可枚举属性都将被枚举。要检查对象是否具有在其*自身*上定义的属性,而不是在其原型链上的某个地方,则有必要使用 [`hasOwnProperty`](/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Object/hasOwnProperty) 或 [`Object.hasOwn`](/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Object/hasOwn) 方法。除 `[[Prototype]]` 为 `null` 的对象外,所有对象都从 `Object.prototype` 继承 [`hasOwnProperty`](/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Object/hasOwnProperty)——除非它已经在原型链较下层处被覆盖。我们将使用上面的图示例代码来说明它,具体如下: ```js function Graph() { @@ -744,7 +613,7 @@ Object.getPrototypeOf(g).hasOwnProperty("addVertex"); // true 对于 Java 或 C++ 的开发者来说,JavaScript 可能有点令人困惑,因为它是完全动态、完全是在执行期间确定的,而且根本没有静态类型。一切都是对象(实例)或函数(构造函数),甚至函数本身也是 `Function` 构造函数的实例。即使是语法结构中的“类”也只是运行时的构造函数。 -JavaScript 中的所有构造函数都有一个被称为 `prototype` 的特殊属性,它与 `new` 运算符一起使用。对原型对象的引用被复制到新实例的内部属性 `[[Prototype]]` 中。例如,当你执行 `const a1 = new A()` 时,JavaScript(在内存中创建对象之后,为其定义 `this` 并执行 `A()` 之前)设置 `a1.[[Prototype]] = A.prototype`。然后,当你访问实例的属性时,JavaScript 首先检查它们是否直接存在于该对象上,如果不存在,则在 `[[Prototype]]` 中查找。会*递归*查询 `[[Prototype]]`,即 `a1.doSomething`、`Object.getPrototypeOf(a1).doSomething`、`Object.getPrototypeOf(Object.getPrototypeOf(a1)).doSomething`,以此类推,直至找到或 `Object.getPrototypeOf` 返回 `null`。这意味着在 `prototype` 上定义的所有属性实际上都由所有实例共享,并且甚至可以更改 `prototype` 的部分内容,使得更改被应用到所有现有的实例中。 +JavaScript 中的所有构造函数都有一个称作 `prototype` 的特殊属性,它与 `new` 运算符一起使用。原型对象的引用复制到新实例的内部属性 `[[Prototype]]` 中。例如,当你执行 `const a1 = new A()` 时,JavaScript(在内存中创建对象之后,用为对象定义的 `this` 运行 `A()` 之前)设置 `a1.[[Prototype]] = A.prototype`。然后,当你访问实例的属性时,JavaScript 首先检查它们是否直接该对象上存在,如果不存在,则在 `[[Prototype]]` 中查找。会*递归*查询 `[[Prototype]]`,即 `a1.doSomething`、`Object.getPrototypeOf(a1).doSomething`、`Object.getPrototypeOf(Object.getPrototypeOf(a1)).doSomething`,以此类推,直至找到或 `Object.getPrototypeOf` 返回 `null`。这意味着在 `prototype` 上定义的所有属性实际上都由所有实例共享,并且甚至可以更改 `prototype` 的部分内容,使得更改被应用到所有现有的实例中。 在上面的示例中,如果你执行 `const a1 = new A(); const a2 = new A();`,那么 `a1.doSomething` 实际上会引用 `Object.getPrototypeOf(a1).doSomething`——这与你定义的 `A.prototype.doSomething` 相同,即 `Object.getPrototypeOf(a1).doSomething === Object.getPrototypeOf(a2).doSomething === A.prototype.doSomething`。 From 778e78485d4d3c66ad1dbf398f4961b167c50e7c Mon Sep 17 00:00:00 2001 From: familyboat <84062528+familyboat@users.noreply.github.com> Date: Thu, 24 Oct 2024 23:43:38 +0800 Subject: [PATCH 03/16] Update files/zh-cn/web/javascript/inheritance_and_the_prototype_chain/index.md Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> --- .../web/javascript/inheritance_and_the_prototype_chain/index.md | 1 + 1 file changed, 1 insertion(+) diff --git a/files/zh-cn/web/javascript/inheritance_and_the_prototype_chain/index.md b/files/zh-cn/web/javascript/inheritance_and_the_prototype_chain/index.md index 9dd73c865fc82e..ae955d66fe717b 100644 --- a/files/zh-cn/web/javascript/inheritance_and_the_prototype_chain/index.md +++ b/files/zh-cn/web/javascript/inheritance_and_the_prototype_chain/index.md @@ -574,6 +574,7 @@ obj.__proto__.__proto__ = { fooProp: "foo val" }; console.log(obj.fooProp); console.log(obj.barProp); ``` + 与 `Object.setPrototypeOf` 相比,将 `__proto__` 设置为非对象的值只会被忽略,而非抛出异常。它也只有稍微好一点的浏览器支持。然而,它是非标准的,并且已废弃。你应该几乎总是使用 `Object.setPrototypeOf` 作为代替。 ## 性能 From 448464ab0204b179d7765e9d2f113bc48c76f1f4 Mon Sep 17 00:00:00 2001 From: familyboat <2015301110129@whu.edu.cn> Date: Fri, 25 Oct 2024 11:41:47 +0800 Subject: [PATCH 04/16] =?UTF-8?q?=E4=BF=AE=E6=AD=A3=E7=9B=B8=E5=85=B3?= =?UTF-8?q?=E9=94=99=E8=AF=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../index.md | 24 ++++++++++--------- 1 file changed, 13 insertions(+), 11 deletions(-) diff --git a/files/zh-cn/web/javascript/inheritance_and_the_prototype_chain/index.md b/files/zh-cn/web/javascript/inheritance_and_the_prototype_chain/index.md index ae955d66fe717b..15aa3262bcafef 100644 --- a/files/zh-cn/web/javascript/inheritance_and_the_prototype_chain/index.md +++ b/files/zh-cn/web/javascript/inheritance_and_the_prototype_chain/index.md @@ -1,6 +1,8 @@ --- title: 继承和原型链 slug: Web/JavaScript/Inheritance_and_the_prototype_chain +l10n: + sourceCommit: e03b13c7e157ec7b7bb02a6c7c4854b862195905 --- {{jsSidebar("Advanced")}} @@ -18,7 +20,7 @@ JavaScript 对于使用过基于类的语言(如 Java 或 C++)的开发者 JavaScript 对象是动态的属性(指**其自有属性**)“包”。JavaScript 对象有一条指向原型对象的链。当试图访问对象的属性时,不仅在该对象上查找属性,还会在该对象的原型上查找属性,以及原型的原型,依次层层向上查找,直到找到一个名字匹配的属性或到达原型链的末尾。 > [!NOTE] -> 遵循 ECMAScript 标准,`someObject.[[Prototype]]` 符合用于标识 `someObject` 的原型。`[[Prototype]]` 内部插槽分别使用 {{jsxref("Object.getPrototypeOf()")}} 和 {{jsxref("Object.setPrototypeOf()")}} 函数访问和修改。其与非标准但许多 JavaScript 引擎实现的 [`__proto__`](/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Object/proto) 访问器等价。为了保持简洁和避免困惑,我们会避免使用 `obj.__proto__` 符号,而是使用 `obj.[[Prototype]]` 作为代替。其对应于 `Object.getPrototypeOf(obj)`。 +> 遵循 ECMAScript 标准,`someObject.[[Prototype]]` 符号用于标识 `someObject` 的原型。`[[Prototype]]` 内部插槽分别使用 {{jsxref("Object.getPrototypeOf()")}} 和 {{jsxref("Object.setPrototypeOf()")}} 函数访问和修改。其与非标准但许多 JavaScript 引擎实现的 [`__proto__`](/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Object/proto) 访问器等价。为了保持简洁和避免困惑,我们会避免使用 `obj.__proto__` 符号,而是使用 `obj.[[Prototype]]` 作为代替。其对应于 `Object.getPrototypeOf(obj)`。 > > 它不应与函数的 `func.prototype` 属性混淆,后者赋值为指定函数用作构造函数时创建的所有对象*实例*的 `[[Prototype]]`。我们将在[后面的小节](#构造函数)中讨论构造函数的 `prototype` 属性。 @@ -93,9 +95,9 @@ console.log(o.d); // 5 ### 继承“方法” -JavaScript 中定义的“[方法](/zh-CN/docs/Glossary/Method)”的形式和基于类的语言定义方法的形式不同。在 JavaScript 中,任何函数都能以属性的形式添加到对象上。继承函数与继承其他属性没有差别,包括上面的属性遮蔽(在函数继承中,称作*方法重写*)。 +JavaScript 中定义“[方法](/zh-CN/docs/Glossary/Method)”的形式和基于类的语言定义方法的形式不同。在 JavaScript 中,任何函数都能以属性的形式添加到对象上。继承的函数与继承的其他属性没有差别,包括上面的属性遮蔽(在继承的函数中,称作*方法重写*)。 -当执行继承函数时,[`this`](/zh-CN/docs/Web/JavaScript/Reference/Operators/this) 值指向的是继承对象,而不是将该函数作为其自有属性的原型对象。 +当执行继承的函数时,[`this`](/zh-CN/docs/Web/JavaScript/Reference/Operators/this) 值指向的是继承对象,而不是将该函数作为其自有属性的原型对象。 ```js const parent = { @@ -129,7 +131,7 @@ console.log(child.method()); // 5 ## 构造函数 -原型的强大之处在于,如果一组属性应该出现在每一个实例上,那我们就可以复用它们——尤其是对于方法。假设我们要创建多个盒子,其中每个盒子都是一个对象,包含一个可以通过 `getValue` 函数访问的值。一个简单的实现可能是: +原型的强大之处在于,如果一组属性应该出现在每一个实例上,那我们就可以复用它们——尤其是对于方法。假设我们要创建多个盒子,其中每个盒子都是一个对象,这个对象包含一个可以通过 `getValue` 函数访问的值。一个简单的实现可能是: ```js-nolint const boxes = [ @@ -349,7 +351,7 @@ console.log(doSomethingFromArrowFunction.prototype); ``` > [!NOTE] -> 遵循规范的术语,Chrome 控制台使用 `[[Prototype]]` 来表示对象的原型;Firefox 使用 ``。为了保持一致性,我们将使用 `[[Prototype]]`。 +> 遵循规范的术语,Chrome 控制台使用 `[[Prototype]]` 表示对象的原型;而 Firefox 使用 ``。为了保持一致性,我们将使用 `[[Prototype]]`。 我们可以像下面这样,向 `doSomething()` 的原型添加属性。 @@ -474,7 +476,7 @@ const p = { b: 2, __proto__: o }; // p ---> o ---> Object.prototype ---> null ``` -在[对象初始化器](/zh-CN/docs/Web/JavaScript/Reference/Operators/Object_initializer)中使用 `__proto__` 键时,将 `__proto__` 键指向非对象的值只会被忽略,而非抛出异常。与 [`Object.prototype.__proto__`](/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Object/proto) 的 setter 相反,对象字面量初始化器中的 `__proto__` 是标准化的、被优化的,甚至比 {{jsxref("Object.create")}} 更高效。在创建对象时声明额外的自有属性比 {{jsxref("Object.create")}} 更符合习惯。 +在[对象初始化器](/zh-CN/docs/Web/JavaScript/Reference/Operators/Object_initializer)中使用 `__proto__` 键时,将 `__proto__` 键指向非对象的值(译者注:`null` 不会被忽略)只会被忽略,而非抛出异常。与 [`Object.prototype.__proto__`](/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Object/proto) 的 setter 相反,对象字面量初始化器中的 `__proto__` 是标准化的、被优化的,甚至比 {{jsxref("Object.create")}} 更高效。在创建对象时声明额外的自有属性比 {{jsxref("Object.create")}} 更符合习惯。 ### 使用构造函数 @@ -493,7 +495,7 @@ const g = new Graph(); // 在执行 new Graph() 时,g.[[Prototype]] 是 Graph.prototype 的值。 ``` -自从非常早期的 JavaScript 开始,构造函数一直可用。因此,它是非常快速、非常标准,以及非常 JIT 优化过的。然而,它也很正确使用,因为以这种方式添加的方法默认是可枚举的,而这与类语法或内置方法的表现行为不一致。构造很长的继承链也容易出错,就像前面展示的那样。 +自从非常早期的 JavaScript 开始,构造函数一直可用。因此,它是非常快速、非常标准,以及非常 JIT 优化过的。然而,它也很难正确使用,因为以这种方式添加的方法默认是可枚举的,而这与类语法或内置方法的表现行为不一致。构造很长的继承链也容易出错,就像前面展示的那样。 ### 使用 Object.create() @@ -518,7 +520,7 @@ console.log(d.hasOwnProperty); 与对象初始化器中的 `__proto__` 键类似,`Object.create()` 允许在创建时直接设置对象的原型,这允许运行时进一步优化对象。还允许使用 `Object.create(null)` 创建原型为 `null` 的对象。`Object.create()` 的第二个参数能精确地指明新对象中每个属性地特性,而这会是一把双刃剑: -- 它能在对象创建期间创建不可枚举地属性,而这用对象字面量做不到。 +- 它能在对象创建期间创建不可枚举的属性,而这用对象字面量做不到。 - 它比对象字面量更加冗余以及更容易出错。 - 它可能比对象字面量更慢,特别是创建很多属性时。 @@ -545,7 +547,7 @@ const filledRectangle = new FilledRectangle(5, 10, "blue"); // filledRectangle ---> FilledRectangle.prototype ---> Rectangle.prototype ---> Object.prototype ---> null ``` -在定义复杂的继承结构式,类提供了非常高的可读性和可维护性。[私有属性](/zh-CN/docs/Web/JavaScript/Reference/Classes/Private_properties)是原型继承中没有简单替代方案的特性。然而,类比传统的构造函数的性能要差,以及不支持旧环境。 +在定义复杂的继承结构时,类提供了非常高的可读性和可维护性。[私有属性](/zh-CN/docs/Web/JavaScript/Reference/Classes/Private_properties)是原型继承中没有简单替代方案的特性。然而,类比传统的构造函数的性能要差,以及不支持旧环境。 ### 使用 Object.setPrototypeOf() @@ -575,7 +577,7 @@ console.log(obj.fooProp); console.log(obj.barProp); ``` -与 `Object.setPrototypeOf` 相比,将 `__proto__` 设置为非对象的值只会被忽略,而非抛出异常。它也只有稍微好一点的浏览器支持。然而,它是非标准的,并且已废弃。你应该几乎总是使用 `Object.setPrototypeOf` 作为代替。 +与 `Object.setPrototypeOf` 相比,将 `__proto__` 设置为非对象的值(译者注:`null` 不会被忽略)只会被忽略,而非抛出异常。它也只有稍微好一点的浏览器支持。然而,它是非标准的,并且已废弃。你应该几乎总是使用 `Object.setPrototypeOf` 作为代替。 ## 性能 @@ -614,7 +616,7 @@ Object.getPrototypeOf(g).hasOwnProperty("addVertex"); // true 对于 Java 或 C++ 的开发者来说,JavaScript 可能有点令人困惑,因为它是完全动态、完全是在执行期间确定的,而且根本没有静态类型。一切都是对象(实例)或函数(构造函数),甚至函数本身也是 `Function` 构造函数的实例。即使是语法结构中的“类”也只是运行时的构造函数。 -JavaScript 中的所有构造函数都有一个称作 `prototype` 的特殊属性,它与 `new` 运算符一起使用。原型对象的引用复制到新实例的内部属性 `[[Prototype]]` 中。例如,当你执行 `const a1 = new A()` 时,JavaScript(在内存中创建对象之后,用为对象定义的 `this` 运行 `A()` 之前)设置 `a1.[[Prototype]] = A.prototype`。然后,当你访问实例的属性时,JavaScript 首先检查它们是否直接该对象上存在,如果不存在,则在 `[[Prototype]]` 中查找。会*递归*查询 `[[Prototype]]`,即 `a1.doSomething`、`Object.getPrototypeOf(a1).doSomething`、`Object.getPrototypeOf(Object.getPrototypeOf(a1)).doSomething`,以此类推,直至找到或 `Object.getPrototypeOf` 返回 `null`。这意味着在 `prototype` 上定义的所有属性实际上都由所有实例共享,并且甚至可以更改 `prototype` 的部分内容,使得更改被应用到所有现有的实例中。 +JavaScript 中的所有构造函数都有一个称作 `prototype` 的特殊属性,它与 `new` 运算符一起使用。原型对象的引用复制到新实例的内部属性 `[[Prototype]]` 中。例如,当你执行 `const a1 = new A()` 时,JavaScript(在内存中创建对象之后,用为对象定义的 `this` 运行 `A()` 之前)设置 `a1.[[Prototype]] = A.prototype`。然后,当你访问实例的属性时,JavaScript 首先检查它们是否直接在该对象上存在,如果不存在,则在 `[[Prototype]]` 中查找。会*递归*查询 `[[Prototype]]`,即 `a1.doSomething`、`Object.getPrototypeOf(a1).doSomething`、`Object.getPrototypeOf(Object.getPrototypeOf(a1)).doSomething`,以此类推,直至找到或 `Object.getPrototypeOf` 返回 `null`。这意味着在 `prototype` 上定义的所有属性实际上都由所有实例共享,并且甚至可以更改 `prototype` 的部分内容,使得更改被应用到所有现有的实例中。 在上面的示例中,如果你执行 `const a1 = new A(); const a2 = new A();`,那么 `a1.doSomething` 实际上会引用 `Object.getPrototypeOf(a1).doSomething`——这与你定义的 `A.prototype.doSomething` 相同,即 `Object.getPrototypeOf(a1).doSomething === Object.getPrototypeOf(a2).doSomething === A.prototype.doSomething`。 From 98812515a9530a94147f0cf8ee54b48520745b61 Mon Sep 17 00:00:00 2001 From: familyboat <2015301110129@whu.edu.cn> Date: Fri, 1 Nov 2024 18:01:50 +0800 Subject: [PATCH 05/16] =?UTF-8?q?=E5=85=A8=20mdn=20=E6=96=87=E6=A1=A3=20?= =?UTF-8?q?=E6=9B=BF=E6=8D=A2=20=E7=BB=A7=E6=89=BF=E5=92=8C=E5=8E=9F?= =?UTF-8?q?=E5=9E=8B=E9=93=BE=20=E4=B8=BA=20=E7=BB=A7=E6=89=BF=E4=B8=8E?= =?UTF-8?q?=E5=8E=9F=E5=9E=8B=E9=93=BE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- files/zh-cn/glossary/class/index.md | 2 +- files/zh-cn/glossary/inheritance/index.md | 2 +- files/zh-cn/glossary/prototype/index.md | 2 +- files/zh-cn/web/javascript/closures/index.md | 2 +- files/zh-cn/web/javascript/index.md | 2 +- .../web/javascript/inheritance_and_the_prototype_chain/index.md | 2 +- .../web/javascript/reference/global_objects/object/index.md | 2 +- 7 files changed, 7 insertions(+), 7 deletions(-) diff --git a/files/zh-cn/glossary/class/index.md b/files/zh-cn/glossary/class/index.md index 21f5a21a84f898..2b5fe9d6b04381 100644 --- a/files/zh-cn/glossary/class/index.md +++ b/files/zh-cn/glossary/class/index.md @@ -12,6 +12,6 @@ l10n: ## 参见 - [JavaScript 中的类](/zh-CN/docs/Learn/JavaScript/Objects/Classes_in_JavaScript) -- [继承和原型链](/zh-CN/docs/Web/JavaScript/Inheritance_and_the_prototype_chain) +- [继承与原型链](/zh-CN/docs/Web/JavaScript/Inheritance_and_the_prototype_chain) - 维基百科上的[基于类编程](https://zh.wikipedia.org/wiki/基于类编程) - 维基百科上的[面向对象程序设计](https://zh.wikipedia.org/wiki/面向对象程序设计) diff --git a/files/zh-cn/glossary/inheritance/index.md b/files/zh-cn/glossary/inheritance/index.md index 72ddca8d6fb8ee..ffe4dbba4d39b0 100644 --- a/files/zh-cn/glossary/inheritance/index.md +++ b/files/zh-cn/glossary/inheritance/index.md @@ -13,4 +13,4 @@ l10n: ## 参见 -- [继承和原型链](/zh-CN/docs/Web/JavaScript/Inheritance_and_the_prototype_chain) +- [继承与原型链](/zh-CN/docs/Web/JavaScript/Inheritance_and_the_prototype_chain) diff --git a/files/zh-cn/glossary/prototype/index.md b/files/zh-cn/glossary/prototype/index.md index 5aef3b6d81ccfa..1d1d3a6a96f123 100644 --- a/files/zh-cn/glossary/prototype/index.md +++ b/files/zh-cn/glossary/prototype/index.md @@ -9,7 +9,7 @@ l10n: 原型是一种在开发生命周期的早期显示应用程序或产品的外观和行为的模型。 -参见[继承和原型链](/zh-CN/docs/Web/JavaScript/Inheritance_and_the_prototype_chain)。 +参见[继承与原型链](/zh-CN/docs/Web/JavaScript/Inheritance_and_the_prototype_chain)。 ## 参见 diff --git a/files/zh-cn/web/javascript/closures/index.md b/files/zh-cn/web/javascript/closures/index.md index 3fdd04c7d80b24..641f8f8d8521ed 100644 --- a/files/zh-cn/web/javascript/closures/index.md +++ b/files/zh-cn/web/javascript/closures/index.md @@ -564,4 +564,4 @@ MyObject.prototype.getMessage = function () { }; ``` -在前面两个示例中,继承的原型由所有的对象共享,因此方法定义不需要出现在对象创建中。参见[继承和原型链](/zh-CN/docs/Web/JavaScript/Inheritance_and_the_prototype_chain)了解更多。 +在前面两个示例中,继承的原型由所有的对象共享,因此方法定义不需要出现在对象创建中。参见[继承与原型链](/zh-CN/docs/Web/JavaScript/Inheritance_and_the_prototype_chain)了解更多。 diff --git a/files/zh-cn/web/javascript/index.md b/files/zh-cn/web/javascript/index.md index e0df3af91f3a78..ebece17542133f 100644 --- a/files/zh-cn/web/javascript/index.md +++ b/files/zh-cn/web/javascript/index.md @@ -65,7 +65,7 @@ JavaScript 核心语言特性(大部分是纯 [ECMAScript](/zh-CN/docs/Web/Jav ### 高级 -- [继承和原型链](/zh-CN/docs/Web/JavaScript/Guide/Inheritance_and_the_prototype_chain) +- [继承与原型链](/zh-CN/docs/Web/JavaScript/Guide/Inheritance_and_the_prototype_chain) - : 解释被广泛误解与低估的基于原型的继承。 - [内存管理](/zh-CN/docs/Web/JavaScript/Memory_management) - : JavaScript 的内存生命周期和垃圾回收。 diff --git a/files/zh-cn/web/javascript/inheritance_and_the_prototype_chain/index.md b/files/zh-cn/web/javascript/inheritance_and_the_prototype_chain/index.md index 15aa3262bcafef..4eda5621bd36cb 100644 --- a/files/zh-cn/web/javascript/inheritance_and_the_prototype_chain/index.md +++ b/files/zh-cn/web/javascript/inheritance_and_the_prototype_chain/index.md @@ -1,5 +1,5 @@ --- -title: 继承和原型链 +title: 继承与原型链 slug: Web/JavaScript/Inheritance_and_the_prototype_chain l10n: sourceCommit: e03b13c7e157ec7b7bb02a6c7c4854b862195905 diff --git a/files/zh-cn/web/javascript/reference/global_objects/object/index.md b/files/zh-cn/web/javascript/reference/global_objects/object/index.md index b94fd7e81f304b..56789c2f20c1b3 100644 --- a/files/zh-cn/web/javascript/reference/global_objects/object/index.md +++ b/files/zh-cn/web/javascript/reference/global_objects/object/index.md @@ -45,7 +45,7 @@ Object.prototype.propertyIsEnumerable.call(obj, "foo"); // true;预期的结 ### null 原型对象 -几乎所有的 JavaScript 对象最终都继承自 `Object.prototype`(参见[继承和原型链](/zh-CN/docs/Web/JavaScript/Inheritance_and_the_prototype_chain))。然而,你可以使用 [`Object.create(null)`](/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Object/create) 或定义了 `__proto__: null` 的[对象字面量语法](/zh-CN/docs/Web/JavaScript/Reference/Operators/Object_initializer)(注意:对象字面量中的 `__proto__` 键不同于已弃用的 [`Object.prototype.__proto__`](/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Object/proto) 属性)来创建 `null` 原型对象。你还可以通过调用 [`Object.setPrototypeOf(obj, null)`](/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Object/setPrototypeOf) 将现有对象的原型更改为 `null`。 +几乎所有的 JavaScript 对象最终都继承自 `Object.prototype`(参见[继承与原型链](/zh-CN/docs/Web/JavaScript/Inheritance_and_the_prototype_chain))。然而,你可以使用 [`Object.create(null)`](/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Object/create) 或定义了 `__proto__: null` 的[对象字面量语法](/zh-CN/docs/Web/JavaScript/Reference/Operators/Object_initializer)(注意:对象字面量中的 `__proto__` 键不同于已弃用的 [`Object.prototype.__proto__`](/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Object/proto) 属性)来创建 `null` 原型对象。你还可以通过调用 [`Object.setPrototypeOf(obj, null)`](/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Object/setPrototypeOf) 将现有对象的原型更改为 `null`。 ```js const obj = Object.create(null); From 960fc5701c160808deeb3627e5fa2c3b230a24af Mon Sep 17 00:00:00 2001 From: familyboat <2015301110129@whu.edu.cn> Date: Fri, 1 Nov 2024 18:17:26 +0800 Subject: [PATCH 06/16] =?UTF-8?q?=E5=B0=86=E4=B8=8D=E5=BF=85=E8=A6=81?= =?UTF-8?q?=E7=9A=84=E4=BF=AE=E6=94=B9=E6=94=B9=E5=9B=9E=E5=8E=BB?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../index.md | 20 +++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/files/zh-cn/web/javascript/inheritance_and_the_prototype_chain/index.md b/files/zh-cn/web/javascript/inheritance_and_the_prototype_chain/index.md index 4eda5621bd36cb..e61a35238e56b0 100644 --- a/files/zh-cn/web/javascript/inheritance_and_the_prototype_chain/index.md +++ b/files/zh-cn/web/javascript/inheritance_and_the_prototype_chain/index.md @@ -24,7 +24,7 @@ JavaScript 对象是动态的属性(指**其自有属性**)“包”。JavaS > > 它不应与函数的 `func.prototype` 属性混淆,后者赋值为指定函数用作构造函数时创建的所有对象*实例*的 `[[Prototype]]`。我们将在[后面的小节](#构造函数)中讨论构造函数的 `prototype` 属性。 -有几种可以指定对象的 `[[Prototype]]` 的方法,这些方法将在[后面的小节](#不同的创建对象和改变原型链的方法)中列出。现在,我们将使用 [`__proto__` 语法](/zh-CN/docs/Web/JavaScript/Reference/Operators/Object_initializer#原型_setter)进行说明。值得注意的是,`{ __proto__: ... }` 语法与 `obj.__proto__` 访问器不同:前者是标准且未废弃的。 +有几种可以指定对象的 `[[Prototype]]` 的方法,这些方法将在[后面的小节](#使用不同的方法来创建对象和改变原型链)中列出。现在,我们将使用 [`__proto__` 语法](/zh-CN/docs/Web/JavaScript/Reference/Operators/Object_initializer#原型_setter)进行说明。值得注意的是,`{ __proto__: ... }` 语法与 `obj.__proto__` 访问器不同:前者是标准且未被废弃的。 在像 `{ a: 1, b: 2, __proto__: c }` 这样的对象字面量中,`c` 值(必须为 `null` 或另一个对象)将变成字面量所表示的对象的 `[[Prototype]]`,而其他键(如 `a` 和 `b`)将变成对象的*自有属性*。这种语法读起来非常自然,因为 `[[Prototype]]` 只是对象的“内部属性”。 @@ -131,7 +131,7 @@ console.log(child.method()); // 5 ## 构造函数 -原型的强大之处在于,如果一组属性应该出现在每一个实例上,那我们就可以复用它们——尤其是对于方法。假设我们要创建多个盒子,其中每个盒子都是一个对象,这个对象包含一个可以通过 `getValue` 函数访问的值。一个简单的实现可能是: +原型的强大之处在于,如果一组属性应该出现在每一个实例上,那我们就可以复用它们——尤其是对于方法。假设我们要创建多个盒子,其中每一个盒子都是一个对象,包含一个可以通过 `getValue` 函数访问的值。一个简单的实现可能是: ```js-nolint const boxes = [ @@ -141,7 +141,7 @@ const boxes = [ ]; ``` -这是不够好的,因为每个实例都有做相同事情的自有函数属性,这是冗余且不必要的。相反,我们可以将 `getValue` 移动到所有盒子的 `[[Prototype]]` 上: +这是不够好的,因为每一个实例都有自己的,做相同事情的函数属性,这是冗余且不必要的。相反,我们可以将 `getValue` 移动到所有盒子的 `[[Prototype]]` 上: ```js const boxPrototype = { @@ -173,7 +173,7 @@ Box.prototype.getValue = function () { const boxes = [new Box(1), new Box(2), new Box(3)]; ``` -我们说 `new Box(1)` 是通过 `Box` 构造函数创建的一个*实例*。`Box.prototype` 与我们之前创建的 `boxPrototype` 并无太大区别——它就是一个普通的对象。通过构造函数创建的每一个实例都会自动将构造函数的 [`prototype`](/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Function/prototype) 属性作为其 `[[Prototype]]`。即,`Object.getPrototypeOf(new Box()) === Box.prototype`。`Constructor.prototype` 默认有一个自有属性:[`constructor`](/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Object/constructor),它引用了构造函数本身。即,`Box.prototype.constructor === Box`。这允许我们在任何实例中访问原始构造函数。 +我们说 `new Box(1)` 是通过 `Box` 构造函数创建的一个*实例*。`Box.prototype` 与我们之前创建的 `boxPrototype` 并无太大区别——它只是一个普通的对象。通过构造函数创建的每一个实例都会自动将构造函数的 [`prototype`](/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Function/prototype) 属性作为其 `[[Prototype]]`。即,`Object.getPrototypeOf(new Box()) === Box.prototype`。`Constructor.prototype` 默认具有一个自有属性:[`constructor`](/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Object/constructor),它引用了构造函数本身。即,`Box.prototype.constructor === Box`。这允许我们在任何实例中访问原始构造函数。 > [!NOTE] > 如果构造函数返回非原始值,则该值将成为 `new` 表达式的结果。在这种情况下,`[[Prototype]]` 可能无法正确绑定——但在实践中应该很少发生。 @@ -193,7 +193,7 @@ class Box { } ``` -类是构造函数的语法糖,这意味着你仍然可以修改 `Box.prototype` 改变所有实例的行为。然而,由于类设计为对底层原型机制的抽象,我们将在本教程中使用更轻量级的构造函数语法,以充分展示原型的工作原理。 +类是构造函数的语法糖,这意味着你仍然可以修改 `Box.prototype` 来改变所有实例的行为。然而,由于类被设计为对底层原型机制的抽象,我们将在本教程中使用更轻量级的构造函数语法,以充分展示原型的工作原理。 因为 `Box.prototype` 引用的对象和所有实例的 `[[Prototype]]` 是同一个对象,所以我们可以通过改变 `Box.prototype` 改变所有实例的行为。 @@ -273,7 +273,7 @@ Map.prototype.get(1); ### 构建更长的继承链 -`Constructor.prototype` 属性将成为构造函数的实例的 `[[Prototype]]`,包括 `Constructor.prototype` 的自有 `[[Prototype]]`。默认情况下,`Constructor.prototype` 是一个*普通对象*——即 `Object.getPrototypeOf(Constructor.prototype) === Object.prototype`。唯一的例外是 `Object.prototype` 本身,其 `[[Prototype]]` 是 `null`——即 `Object.getPrototypeOf(Object.prototype) === null`。因此,一个典型的构造函数将构建以下原型链: +`Constructor.prototype` 属性将成为构造函数实例的 `[[Prototype]]`,包括 `Constructor.prototype` 自身的 `[[Prototype]]`。默认情况下,`Constructor.prototype` 是一个*普通对象*——即 `Object.getPrototypeOf(Constructor.prototype) === Object.prototype`。唯一的例外是 `Object.prototype` 本身,其 `[[Prototype]]` 是 `null`——即 `Object.getPrototypeOf(Object.prototype) === null`。因此,一个典型的构造函数将构建以下原型链: ```js function Constructor() {} @@ -305,14 +305,14 @@ const obj = new Derived(); // obj ---> Derived.prototype ---> Base.prototype ---> Object.prototype ---> null ``` -你可能还会看到一些使用 {{jsxref("Object.create()")}} 构建继承链的旧代码。然而,因为这会重新为 `prototype` 属性赋值并删除 [`constructor`](/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Object/constructor) 属性,所以更容易出错,而且如果构造函数还没有创建任何实例,性能提升可能并不明显。 +你可能还会看到一些使用 {{jsxref("Object.create()")}} 来构建继承链的旧代码。然而,因为这会重新为 `prototype` 属性赋值并删除 [`constructor`](/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Object/constructor) 属性,所以更容易出错,而且如果构造函数还没有创建任何实例,性能提升可能并不明显。 ```js example-bad function Base() {} function Derived() {} // 将 `Derived.prototype` 重新赋值为一个新对象, // `Base.prototype` 作为新对象的 `[[Prototype]]` -// 请不要这样做——使用 Object.setPrototypeOf 修改它 +// 请不要这样做——使用 Object.setPrototypeOf 来修改它 Derived.prototype = Object.create(Base.prototype); ``` @@ -446,7 +446,7 @@ doSomething.prototype.prop: undefined doSomething.prototype.foo: bar ``` -## 不同的创建对象和改变原型链的方法 +## 使用不同的方法来创建对象和改变原型链 我们碰到过很多创建对象和改变其原型链的方法。我们将系统地总结不同的方法,并比较每种方法的优缺点。 @@ -566,7 +566,7 @@ Object.setPrototypeOf(obj, anotherObj); 所有对象都继承了 [`Object.prototype.__proto__`](/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Object/proto) setter,它可以用来设置现有对象的 `[[Prototype]]`(如果对象没有覆盖 `__proto__` 属性)。 -> **警告:** `Object.prototype.__proto__` 访问器是**非标准**的,且已废弃。你应该几乎总是使用 `Object.setPrototypeOf` 作为代替。 +> **警告:** `Object.prototype.__proto__` 访问器是**非标准**的,且已被弃用。你应该几乎总是使用 `Object.setPrototypeOf` 作为代替。 ```js const obj = {}; From b496685014c9bb3535f7bbab70c8ed2b4ee0414e Mon Sep 17 00:00:00 2001 From: familyboat <2015301110129@whu.edu.cn> Date: Fri, 1 Nov 2024 18:21:45 +0800 Subject: [PATCH 07/16] =?UTF-8?q?=E4=BD=BF=E7=94=A8=E8=A7=84=E8=8C=83?= =?UTF-8?q?=E7=94=A8=E8=AF=AD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../web/javascript/inheritance_and_the_prototype_chain/index.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/files/zh-cn/web/javascript/inheritance_and_the_prototype_chain/index.md b/files/zh-cn/web/javascript/inheritance_and_the_prototype_chain/index.md index e61a35238e56b0..74bbf1c01a4475 100644 --- a/files/zh-cn/web/javascript/inheritance_and_the_prototype_chain/index.md +++ b/files/zh-cn/web/javascript/inheritance_and_the_prototype_chain/index.md @@ -24,7 +24,7 @@ JavaScript 对象是动态的属性(指**其自有属性**)“包”。JavaS > > 它不应与函数的 `func.prototype` 属性混淆,后者赋值为指定函数用作构造函数时创建的所有对象*实例*的 `[[Prototype]]`。我们将在[后面的小节](#构造函数)中讨论构造函数的 `prototype` 属性。 -有几种可以指定对象的 `[[Prototype]]` 的方法,这些方法将在[后面的小节](#使用不同的方法来创建对象和改变原型链)中列出。现在,我们将使用 [`__proto__` 语法](/zh-CN/docs/Web/JavaScript/Reference/Operators/Object_initializer#原型_setter)进行说明。值得注意的是,`{ __proto__: ... }` 语法与 `obj.__proto__` 访问器不同:前者是标准且未被废弃的。 +有几种可以指定对象的 `[[Prototype]]` 的方法,这些方法将在[后面的小节](#使用不同的方法来创建对象和改变原型链)中列出。现在,我们将使用 [`__proto__` 语法](/zh-CN/docs/Web/JavaScript/Reference/Operators/Object_initializer#原型_setter)进行说明。值得注意的是,`{ __proto__: ... }` 语法与 `obj.__proto__` 访问器不同:前者是标准且未被弃用的。 在像 `{ a: 1, b: 2, __proto__: c }` 这样的对象字面量中,`c` 值(必须为 `null` 或另一个对象)将变成字面量所表示的对象的 `[[Prototype]]`,而其他键(如 `a` 和 `b`)将变成对象的*自有属性*。这种语法读起来非常自然,因为 `[[Prototype]]` 只是对象的“内部属性”。 From 3280098a1be4af0f44a97f95334ad9050c094494 Mon Sep 17 00:00:00 2001 From: familyboat <84062528+familyboat@users.noreply.github.com> Date: Mon, 4 Nov 2024 20:57:28 +0800 Subject: [PATCH 08/16] Update files/zh-cn/web/javascript/inheritance_and_the_prototype_chain/index.md Co-authored-by: A1lo --- .../web/javascript/inheritance_and_the_prototype_chain/index.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/files/zh-cn/web/javascript/inheritance_and_the_prototype_chain/index.md b/files/zh-cn/web/javascript/inheritance_and_the_prototype_chain/index.md index 74bbf1c01a4475..e15555f45bc8fb 100644 --- a/files/zh-cn/web/javascript/inheritance_and_the_prototype_chain/index.md +++ b/files/zh-cn/web/javascript/inheritance_and_the_prototype_chain/index.md @@ -195,7 +195,7 @@ class Box { 类是构造函数的语法糖,这意味着你仍然可以修改 `Box.prototype` 来改变所有实例的行为。然而,由于类被设计为对底层原型机制的抽象,我们将在本教程中使用更轻量级的构造函数语法,以充分展示原型的工作原理。 -因为 `Box.prototype` 引用的对象和所有实例的 `[[Prototype]]` 是同一个对象,所以我们可以通过改变 `Box.prototype` 改变所有实例的行为。 +因为 `Box.prototype` 引用的对象和所有实例的 `[[Prototype]]` 是同一个对象,所以我们可以通过改变 `Box.prototype` 来改变所有实例的行为。 ```js function Box(value) { From 76e2b54830c52d2ddf139891c85382c217c073f0 Mon Sep 17 00:00:00 2001 From: familyboat <84062528+familyboat@users.noreply.github.com> Date: Mon, 4 Nov 2024 20:59:26 +0800 Subject: [PATCH 09/16] Update files/zh-cn/web/javascript/inheritance_and_the_prototype_chain/index.md Co-authored-by: A1lo --- .../web/javascript/inheritance_and_the_prototype_chain/index.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/files/zh-cn/web/javascript/inheritance_and_the_prototype_chain/index.md b/files/zh-cn/web/javascript/inheritance_and_the_prototype_chain/index.md index e15555f45bc8fb..7d81a3d808a420 100644 --- a/files/zh-cn/web/javascript/inheritance_and_the_prototype_chain/index.md +++ b/files/zh-cn/web/javascript/inheritance_and_the_prototype_chain/index.md @@ -311,7 +311,7 @@ const obj = new Derived(); function Base() {} function Derived() {} // 将 `Derived.prototype` 重新赋值为一个新对象, -// `Base.prototype` 作为新对象的 `[[Prototype]]` +// 并将 `Base.prototype` 作为新对象的 `[[Prototype]]` // 请不要这样做——使用 Object.setPrototypeOf 来修改它 Derived.prototype = Object.create(Base.prototype); ``` From e69429c3fe28508171a6f1157a05dffdc8f74f00 Mon Sep 17 00:00:00 2001 From: familyboat <84062528+familyboat@users.noreply.github.com> Date: Mon, 4 Nov 2024 20:59:57 +0800 Subject: [PATCH 10/16] Update files/zh-cn/web/javascript/inheritance_and_the_prototype_chain/index.md Co-authored-by: A1lo --- .../web/javascript/inheritance_and_the_prototype_chain/index.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/files/zh-cn/web/javascript/inheritance_and_the_prototype_chain/index.md b/files/zh-cn/web/javascript/inheritance_and_the_prototype_chain/index.md index 7d81a3d808a420..014938bbc6914b 100644 --- a/files/zh-cn/web/javascript/inheritance_and_the_prototype_chain/index.md +++ b/files/zh-cn/web/javascript/inheritance_and_the_prototype_chain/index.md @@ -518,7 +518,7 @@ console.log(d.hasOwnProperty); // undefined,因为 d 没有继承 Object.prototype ``` -与对象初始化器中的 `__proto__` 键类似,`Object.create()` 允许在创建时直接设置对象的原型,这允许运行时进一步优化对象。还允许使用 `Object.create(null)` 创建原型为 `null` 的对象。`Object.create()` 的第二个参数能精确地指明新对象中每个属性地特性,而这会是一把双刃剑: +与对象初始化器中的 `__proto__` 键类似,`Object.create()` 允许在创建时直接设置对象的原型,这允许运行时进一步优化对象。还允许使用 `Object.create(null)` 创建原型为 `null` 的对象。`Object.create()` 的第二个参数能精确地指明新对象中每个属性的特性,而这会是一把双刃剑: - 它能在对象创建期间创建不可枚举的属性,而这用对象字面量做不到。 - 它比对象字面量更加冗余以及更容易出错。 From f47703b78e1fe13c3636dfeeb0226d8837b90007 Mon Sep 17 00:00:00 2001 From: familyboat <84062528+familyboat@users.noreply.github.com> Date: Mon, 4 Nov 2024 21:01:24 +0800 Subject: [PATCH 11/16] Update files/zh-cn/web/javascript/inheritance_and_the_prototype_chain/index.md Co-authored-by: A1lo --- .../web/javascript/inheritance_and_the_prototype_chain/index.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/files/zh-cn/web/javascript/inheritance_and_the_prototype_chain/index.md b/files/zh-cn/web/javascript/inheritance_and_the_prototype_chain/index.md index 014938bbc6914b..6675408f47f718 100644 --- a/files/zh-cn/web/javascript/inheritance_and_the_prototype_chain/index.md +++ b/files/zh-cn/web/javascript/inheritance_and_the_prototype_chain/index.md @@ -547,7 +547,7 @@ const filledRectangle = new FilledRectangle(5, 10, "blue"); // filledRectangle ---> FilledRectangle.prototype ---> Rectangle.prototype ---> Object.prototype ---> null ``` -在定义复杂的继承结构时,类提供了非常高的可读性和可维护性。[私有属性](/zh-CN/docs/Web/JavaScript/Reference/Classes/Private_properties)是原型继承中没有简单替代方案的特性。然而,类比传统的构造函数的性能要差,以及不支持旧环境。 +在定义复杂的继承结构时,类提供了非常高的可读性和可维护性。[私有属性](/zh-CN/docs/Web/JavaScript/Reference/Classes/Private_properties)是原型继承中没有简单替代方案的特性。然而,类比传统的构造函数的性能要差,而且不支持旧环境。 ### 使用 Object.setPrototypeOf() From 14f8bcbbfb6dc6b0e5dc712132752bb5859313a5 Mon Sep 17 00:00:00 2001 From: familyboat <2015301110129@whu.edu.cn> Date: Tue, 5 Nov 2024 21:54:06 +0800 Subject: [PATCH 12/16] =?UTF-8?q?=E5=BA=94=E7=94=A8=E8=AF=84=E8=AE=BA?= =?UTF-8?q?=E6=84=8F=E8=A7=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../inheritance_and_the_prototype_chain/index.md | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/files/zh-cn/web/javascript/inheritance_and_the_prototype_chain/index.md b/files/zh-cn/web/javascript/inheritance_and_the_prototype_chain/index.md index 6675408f47f718..ddf89019b8f7d5 100644 --- a/files/zh-cn/web/javascript/inheritance_and_the_prototype_chain/index.md +++ b/files/zh-cn/web/javascript/inheritance_and_the_prototype_chain/index.md @@ -215,8 +215,8 @@ box.getValue(); // 2 有个推论是:_重新赋值_ `Constructor.prototype`(`Constructor.prototype = ...`)是一个不好的主意,原因有两点: -- 在重新赋值之前创建的实例的 `[[Prototype]]` 引用的对象现在与重新赋值之后创建的实例的 `[[Prototype]]` 引用的对象不同——改变一个的 `[[Prototype]]` 不会改变另一个的 `[[Prototype]]`。 -- 除非你手动重新设置 `constructor` 属性,否则无法再通过 `instance.constructor` 追踪到构造函数,这可能会打破用户期待。一些内置操作也会读取 `constructor` 属性,如果没有设置,它们可能无法按预期工作。 +- 在重新赋值之前创建的实例的 `[[Prototype]]` 现在引用的是与重新赋值之后创建的实例的 `[[Prototype]]` 不同的对象——改变一个的 `[[Prototype]]` 不再改变另一个的 `[[Prototype]]`。 +- 除非你手动重新设置 `constructor` 属性,否则无法再通过 `instance.constructor` 追踪到构造函数,这可能会破坏用户期望的行为。一些内置操作也会读取 `constructor` 属性,如果没有设置,它们可能无法按预期工作。 `Constructor.prototype` 仅在构造实例时有用。它与 `Constructor.[[Prototype]]` 无关,后者是构造函数的*自有*原型,即 `Function.prototype`。也就是说,`Object.getPrototypeOf(Constructor) === Function.prototype`。 @@ -476,7 +476,7 @@ const p = { b: 2, __proto__: o }; // p ---> o ---> Object.prototype ---> null ``` -在[对象初始化器](/zh-CN/docs/Web/JavaScript/Reference/Operators/Object_initializer)中使用 `__proto__` 键时,将 `__proto__` 键指向非对象的值(译者注:`null` 不会被忽略)只会被忽略,而非抛出异常。与 [`Object.prototype.__proto__`](/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Object/proto) 的 setter 相反,对象字面量初始化器中的 `__proto__` 是标准化的、被优化的,甚至比 {{jsxref("Object.create")}} 更高效。在创建对象时声明额外的自有属性比 {{jsxref("Object.create")}} 更符合习惯。 +在[对象初始化器](/zh-CN/docs/Web/JavaScript/Reference/Operators/Object_initializer)中使用 `__proto__` 键时,将 `__proto__` 键指向非对象的值只会被忽略,而非抛出异常。与 [`Object.prototype.__proto__`](/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Object/proto) 的 setter 相反,对象字面量初始化器中的 `__proto__` 是标准化的、被优化的,甚至比 {{jsxref("Object.create")}} 更高效。在创建对象时声明额外的自有属性比 {{jsxref("Object.create")}} 更符合习惯。 ### 使用构造函数 @@ -495,7 +495,7 @@ const g = new Graph(); // 在执行 new Graph() 时,g.[[Prototype]] 是 Graph.prototype 的值。 ``` -自从非常早期的 JavaScript 开始,构造函数一直可用。因此,它是非常快速、非常标准,以及非常 JIT 优化过的。然而,它也很难正确使用,因为以这种方式添加的方法默认是可枚举的,而这与类语法或内置方法的表现行为不一致。构造很长的继承链也容易出错,就像前面展示的那样。 +构造函数从很早期的 JavaScript 就已经存在了。因此,它的速度非常快,非常标准,并且非常容易被 JIT 优化。然而,它也很难“正确地”使用,因为通过这种方式添加的方法默认是可枚举的,这与类语法或内置方法的行为不一致。进行更长的继承链也容易出错,正如之前所展示的那样。 ### 使用 Object.create() @@ -507,7 +507,7 @@ const a = { a: 1 }; const b = Object.create(a); // b ---> a ---> Object.prototype ---> null -console.log(b.a); // 1 (继承的) +console.log(b.a); // 1(继承的) const c = Object.create(b); // c ---> b ---> a ---> Object.prototype ---> null @@ -521,7 +521,7 @@ console.log(d.hasOwnProperty); 与对象初始化器中的 `__proto__` 键类似,`Object.create()` 允许在创建时直接设置对象的原型,这允许运行时进一步优化对象。还允许使用 `Object.create(null)` 创建原型为 `null` 的对象。`Object.create()` 的第二个参数能精确地指明新对象中每个属性的特性,而这会是一把双刃剑: - 它能在对象创建期间创建不可枚举的属性,而这用对象字面量做不到。 -- 它比对象字面量更加冗余以及更容易出错。 +- 它比对象字面量更加冗长以及更容易出错。 - 它可能比对象字面量更慢,特别是创建很多属性时。 ### 使用类 @@ -577,7 +577,7 @@ console.log(obj.fooProp); console.log(obj.barProp); ``` -与 `Object.setPrototypeOf` 相比,将 `__proto__` 设置为非对象的值(译者注:`null` 不会被忽略)只会被忽略,而非抛出异常。它也只有稍微好一点的浏览器支持。然而,它是非标准的,并且已废弃。你应该几乎总是使用 `Object.setPrototypeOf` 作为代替。 +与 `Object.setPrototypeOf` 相比,将 `__proto__` 设置为非对象的值只会被忽略,而非抛出异常。它也只有稍微好一点的浏览器支持。然而,它是非标准的,并且已废弃。你应该几乎总是使用 `Object.setPrototypeOf` 作为代替。 ## 性能 @@ -616,7 +616,7 @@ Object.getPrototypeOf(g).hasOwnProperty("addVertex"); // true 对于 Java 或 C++ 的开发者来说,JavaScript 可能有点令人困惑,因为它是完全动态、完全是在执行期间确定的,而且根本没有静态类型。一切都是对象(实例)或函数(构造函数),甚至函数本身也是 `Function` 构造函数的实例。即使是语法结构中的“类”也只是运行时的构造函数。 -JavaScript 中的所有构造函数都有一个称作 `prototype` 的特殊属性,它与 `new` 运算符一起使用。原型对象的引用复制到新实例的内部属性 `[[Prototype]]` 中。例如,当你执行 `const a1 = new A()` 时,JavaScript(在内存中创建对象之后,用为对象定义的 `this` 运行 `A()` 之前)设置 `a1.[[Prototype]] = A.prototype`。然后,当你访问实例的属性时,JavaScript 首先检查它们是否直接在该对象上存在,如果不存在,则在 `[[Prototype]]` 中查找。会*递归*查询 `[[Prototype]]`,即 `a1.doSomething`、`Object.getPrototypeOf(a1).doSomething`、`Object.getPrototypeOf(Object.getPrototypeOf(a1)).doSomething`,以此类推,直至找到或 `Object.getPrototypeOf` 返回 `null`。这意味着在 `prototype` 上定义的所有属性实际上都由所有实例共享,并且甚至可以更改 `prototype` 的部分内容,使得更改被应用到所有现有的实例中。 +JavaScript 中的所有构造函数都有一个被称为 `prototype` 的特殊属性,它与 `new` 运算符一起使用。对原型对象的引用被复制到新实例的内部属性 `[[Prototype]]` 中。例如,当你执行 `const a1 = new A()` 时,JavaScript(在内存中创建对象之后,定义 `this` 为该对象并执行 `A()` 之前)设置 `a1.[[Prototype]] = A.prototype`。然后,当你访问实例的属性时,JavaScript 首先检查它们是否直接存在于该对象上,如果不存在,则在 `[[Prototype]]` 中查找。会*递归*查询 `[[Prototype]]`,即 `a1.doSomething`、`Object.getPrototypeOf(a1).doSomething`、`Object.getPrototypeOf(Object.getPrototypeOf(a1)).doSomething`,以此类推,直至找到或 `Object.getPrototypeOf` 返回 `null`。这意味着在 `prototype` 上定义的所有属性实际上都由所有实例共享,并且甚至可以更改 `prototype` 的部分内容,使得更改被应用到所有现有的实例中。 在上面的示例中,如果你执行 `const a1 = new A(); const a2 = new A();`,那么 `a1.doSomething` 实际上会引用 `Object.getPrototypeOf(a1).doSomething`——这与你定义的 `A.prototype.doSomething` 相同,即 `Object.getPrototypeOf(a1).doSomething === Object.getPrototypeOf(a2).doSomething === A.prototype.doSomething`。 From 12f26c53cad68a025adbc5338f958d4ae2d125e8 Mon Sep 17 00:00:00 2001 From: familyboat <84062528+familyboat@users.noreply.github.com> Date: Tue, 5 Nov 2024 21:57:47 +0800 Subject: [PATCH 13/16] Update files/zh-cn/web/javascript/inheritance_and_the_prototype_chain/index.md Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> --- .../web/javascript/inheritance_and_the_prototype_chain/index.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/files/zh-cn/web/javascript/inheritance_and_the_prototype_chain/index.md b/files/zh-cn/web/javascript/inheritance_and_the_prototype_chain/index.md index ddf89019b8f7d5..25b0c8511c7ce7 100644 --- a/files/zh-cn/web/javascript/inheritance_and_the_prototype_chain/index.md +++ b/files/zh-cn/web/javascript/inheritance_and_the_prototype_chain/index.md @@ -495,7 +495,7 @@ const g = new Graph(); // 在执行 new Graph() 时,g.[[Prototype]] 是 Graph.prototype 的值。 ``` -构造函数从很早期的 JavaScript 就已经存在了。因此,它的速度非常快,非常标准,并且非常容易被 JIT 优化。然而,它也很难“正确地”使用,因为通过这种方式添加的方法默认是可枚举的,这与类语法或内置方法的行为不一致。进行更长的继承链也容易出错,正如之前所展示的那样。 +构造函数从很早期的 JavaScript 就已经存在了。因此,它的速度非常快,非常标准,并且非常容易被 JIT 优化。然而,它也很难“正确地”使用,因为通过这种方式添加的方法默认是可枚举的,这与类语法或内置方法的行为不一致。进行更长的继承链也容易出错,正如之前所展示的那样。 ### 使用 Object.create() From 4f42dd0b0b9b9a1cffd79dea00100d0e22bec293 Mon Sep 17 00:00:00 2001 From: familyboat <2015301110129@whu.edu.cn> Date: Wed, 6 Nov 2024 00:06:16 +0800 Subject: [PATCH 14/16] =?UTF-8?q?=E5=BA=94=E7=94=A8=E8=AF=84=E8=AE=BA?= =?UTF-8?q?=E6=84=8F=E8=A7=81=EF=BC=8C=E5=B9=B6=E8=BF=9B=E8=A1=8C=E9=83=A8?= =?UTF-8?q?=E5=88=86=E4=BF=AE=E6=94=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../index.md | 59 +++++++++---------- 1 file changed, 29 insertions(+), 30 deletions(-) diff --git a/files/zh-cn/web/javascript/inheritance_and_the_prototype_chain/index.md b/files/zh-cn/web/javascript/inheritance_and_the_prototype_chain/index.md index 25b0c8511c7ce7..15d17ddaa757da 100644 --- a/files/zh-cn/web/javascript/inheritance_and_the_prototype_chain/index.md +++ b/files/zh-cn/web/javascript/inheritance_and_the_prototype_chain/index.md @@ -7,26 +7,26 @@ l10n: {{jsSidebar("Advanced")}} -在编程中,*继承*是指从父代向下传递特性给子代,这样新代码就能复用旧代码的特性以及在旧代码的特性上进行构建。JavaScript 使用[对象](/zh-CN/docs/Web/JavaScript/Data_structures#object)实现继承。每个对象都有一条链接到另一个称作*原型*的对象的内部链。该原型对象也有它自己的原型,直到将 `null` 作为原型的那个对象。根据定义,`null` 没有原型,它就像是这条*原型链*中最后的一环。在运行时修改原型链的任何成员、甚至是换掉原型都是可能的,所以 JavaScript 中不存在[静态分派](https://en.wikipedia.org/wiki/Static_dispatch)的概念。 +在编程中,*继承*是指将特性从父代传递给子代,以便新代码可以重用并基于现有代码的特性进行构建。JavaScript 使用[对象](/zh-CN/docs/Web/JavaScript/Data_structures#object)实现继承。每个对象都有一条链接到另一个称作*原型*的对象的内部链。该原型对象有自己的原型,依此类推,直到原型是 `null` 的对象。根据定义,`null` 没有原型,并作为这条*原型链*中最后的一环。在运行时修改原型链的任何成员、甚至是换掉原型都是可能的,所以像[静态分派](https://en.wikipedia.org/wiki/Static_dispatch)这样的概念在 JavaScript 中不存在。 -JavaScript 对于使用过基于类的语言(如 Java 或 C++)的开发者来说,有些令人困惑——因为它是[动态的](/zh-CN/docs/Web/JavaScript/Data_structures#动态类型和弱类型)并且没有静态类型。尽管这个困惑通常认为是 JavaScript 的弱点之一,但是原型继承模型本身实际上比类式模型更强大。例如,在原型模型的基础上构建类式模型(即[类](/zh-CN/docs/Web/JavaScript/Reference/Classes)的实现方式)相当简单。 +对于有基于类的语言(如 Java 或 C++)经验的开发者来说,JavaScript 有些令人困惑——因为它是[动态的](/zh-CN/docs/Web/JavaScript/Data_structures#动态类型和弱类型)并且没有静态类型。尽管这个困惑通常被认为是 JavaScript 的弱点之一,但是原型继承模型实际上比类式模型更强大。例如,在原型模型的基础上构建类式模型(即[类](/zh-CN/docs/Web/JavaScript/Reference/Classes)的实现方式)相当简单。 -尽管类现在被广泛使用并成为 JavaScript 中新的范式,但类并没有引入新的继承模式。尽管类抽象掉了大部分的原型机制,但理解原型在底层是如何工作的仍然十分有用。 +尽管类现在被广泛使用并成为 JavaScript 中新的范式,但是类并没有引入新的继承模式。尽管类抽象掉了大部分的原型机制,但是理解原型的底层工作机制仍然十分有用。 ## 基于原型链的继承 ### 继承属性 -JavaScript 对象是动态的属性(指**其自有属性**)“包”。JavaScript 对象有一条指向原型对象的链。当试图访问对象的属性时,不仅在该对象上查找属性,还会在该对象的原型上查找属性,以及原型的原型,依次层层向上查找,直到找到一个名字匹配的属性或到达原型链的末尾。 +JavaScript 对象是动态的属性(称为**自有属性**)“包”。JavaScript 对象有一条指向原型对象的链。当试图访问对象的属性时,不仅在该对象上查找属性,还会在该对象的原型上查找属性,以及原型的原型,依此类推,直到找到一个名字匹配的属性或到达原型链的末尾。 > [!NOTE] -> 遵循 ECMAScript 标准,`someObject.[[Prototype]]` 符号用于标识 `someObject` 的原型。`[[Prototype]]` 内部插槽分别使用 {{jsxref("Object.getPrototypeOf()")}} 和 {{jsxref("Object.setPrototypeOf()")}} 函数访问和修改。其与非标准但许多 JavaScript 引擎实现的 [`__proto__`](/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Object/proto) 访问器等价。为了保持简洁和避免困惑,我们会避免使用 `obj.__proto__` 符号,而是使用 `obj.[[Prototype]]` 作为代替。其对应于 `Object.getPrototypeOf(obj)`。 +> 根据 ECMAScript 标准,符号 `someObject.[[Prototype]]` 用于指定 `someObject` 的原型。使用 {{jsxref("Object.getPrototypeOf()")}} 和 {{jsxref("Object.setPrototypeOf()")}} 函数分别访问和修改 `[[Prototype]]` 内部插槽。这与 JavaScript 访问器 [`__proto__`](/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Object/proto) 是等价的,后者是非标准的,但许多 JavaScript 引擎实际上实现了它。为了保持简洁和避免困惑,在我们的表示法中,我们会避免使用 `obj.__proto__`,而是使用 `obj.[[Prototype]]`。其对应于 `Object.getPrototypeOf(obj)`。 > -> 它不应与函数的 `func.prototype` 属性混淆,后者赋值为指定函数用作构造函数时创建的所有对象*实例*的 `[[Prototype]]`。我们将在[后面的小节](#构造函数)中讨论构造函数的 `prototype` 属性。 +> 不应将它与函数的 `func.prototype` 属性弄混,后者表明的是指定函数作为构造函数时创建的所有对象*实例*的 `[[Prototype]]`。我们将在[后面的小节](#构造函数)中讨论构造函数的 `prototype` 属性。 有几种可以指定对象的 `[[Prototype]]` 的方法,这些方法将在[后面的小节](#使用不同的方法来创建对象和改变原型链)中列出。现在,我们将使用 [`__proto__` 语法](/zh-CN/docs/Web/JavaScript/Reference/Operators/Object_initializer#原型_setter)进行说明。值得注意的是,`{ __proto__: ... }` 语法与 `obj.__proto__` 访问器不同:前者是标准且未被弃用的。 -在像 `{ a: 1, b: 2, __proto__: c }` 这样的对象字面量中,`c` 值(必须为 `null` 或另一个对象)将变成字面量所表示的对象的 `[[Prototype]]`,而其他键(如 `a` 和 `b`)将变成对象的*自有属性*。这种语法读起来非常自然,因为 `[[Prototype]]` 只是对象的“内部属性”。 +在像 `{ a: 1, b: 2, __proto__: c }` 这样的对象字面量中,值 `c`(其必须为 `null` 或另一个对象)将变成字面量所表示的对象的 `[[Prototype]]`,而其他像 `a` 和 `b` 这样的键将变成对象的*自有属性*。这种语法读起来非常自然,因为 `[[Prototype]]` 只是对象的“内部属性”。 下面演示当尝试访问属性时会发生什么: @@ -34,7 +34,7 @@ JavaScript 对象是动态的属性(指**其自有属性**)“包”。JavaS const o = { a: 1, b: 2, - // __proto__ 设置了 [[Prototype]]。它在这里被指定为另一个对象字面量。 + // __proto__ 设置了 [[Prototype]]。在这里它被指定为另一个对象字面量。 __proto__: { b: 3, c: 4, @@ -54,8 +54,8 @@ console.log(o.a); // 1 console.log(o.b); // 2 // o 上有自有属性“b”吗?有,且其值为 2。 -// 原型也有属性“b”,但其没有被访问。 -// 这叫做属性遮蔽(Property Shadowing) +// 原型也有“b”属性,但其没有被访问。 +// 这被称为属性遮蔽(Property Shadowing) console.log(o.c); // 4 // o 上有自有属性“c”吗?没有,检查其原型。 @@ -65,7 +65,7 @@ console.log(o.d); // undefined // o 上有自有属性“d”吗?没有,检查其原型。 // o.[[Prototype]] 上有自有属性“d”吗?没有,检查其原型。 // o.[[Prototype]].[[Prototype]] 是 Object.prototype 且 -// 其默认没有属性“d”,检查其原型。 +// 其默认没有“d”属性,检查其原型。 // o.[[Prototype]].[[Prototype]].[[Prototype]] 为 null,停止搜索, // 未找到该属性,返回 undefined。 ``` @@ -78,7 +78,7 @@ console.log(o.d); // undefined const o = { a: 1, b: 2, - // __proto__ 设置了 [[Prototype]]。它在这里被指定为另一个对象字面量。 + // __proto__ 设置了 [[Prototype]]。在这里它被指定为另一个对象字面量。 __proto__: { b: 3, c: 4, @@ -95,9 +95,9 @@ console.log(o.d); // 5 ### 继承“方法” -JavaScript 中定义“[方法](/zh-CN/docs/Glossary/Method)”的形式和基于类的语言定义方法的形式不同。在 JavaScript 中,任何函数都能以属性的形式添加到对象上。继承的函数与继承的其他属性没有差别,包括上面的属性遮蔽(在继承的函数中,称作*方法重写*)。 +JavaScript 中定义“[方法](/zh-CN/docs/Glossary/Method)”的形式和基于类的语言定义方法的形式不同。在 JavaScript 中,对象可以以属性的形式添加函数。继承的函数与其他属性一样,包括属性遮蔽(在这种情况下,是一种*方法重写*的形式)。 -当执行继承的函数时,[`this`](/zh-CN/docs/Web/JavaScript/Reference/Operators/this) 值指向的是继承对象,而不是将该函数作为其自有属性的原型对象。 +当执行继承的函数时,[`this`](/zh-CN/docs/Web/JavaScript/Reference/Operators/this) 值指向继承对象,而不是将该函数作为其自有属性的原型对象。 ```js const parent = { @@ -117,11 +117,11 @@ const child = { console.log(child.method()); // 3 // 调用 child.method 时,“this”指向了 child。 // 又因为 child 继承的是 parent 的方法, -// 首先在 child 上寻找“value”属性。 +// 首先在 child 上寻找属性“value”。 // 然而,因为 child 没有名为“value”的自有属性, // 该属性会在 [[Prototype]] 上被找到,即 parent.value。 -child.value = 4; // 将 child 上的“value”属性赋值为 4。 +child.value = 4; // 将 child 上的属性“value”赋值为 4。 // 这会遮蔽 parent 上的“value”属性。 // child 对象现在看起来是这样的: // { value: 4, __proto__: { value: 2, method: [Function] } } @@ -176,9 +176,9 @@ const boxes = [new Box(1), new Box(2), new Box(3)]; 我们说 `new Box(1)` 是通过 `Box` 构造函数创建的一个*实例*。`Box.prototype` 与我们之前创建的 `boxPrototype` 并无太大区别——它只是一个普通的对象。通过构造函数创建的每一个实例都会自动将构造函数的 [`prototype`](/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Function/prototype) 属性作为其 `[[Prototype]]`。即,`Object.getPrototypeOf(new Box()) === Box.prototype`。`Constructor.prototype` 默认具有一个自有属性:[`constructor`](/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Object/constructor),它引用了构造函数本身。即,`Box.prototype.constructor === Box`。这允许我们在任何实例中访问原始构造函数。 > [!NOTE] -> 如果构造函数返回非原始值,则该值将成为 `new` 表达式的结果。在这种情况下,`[[Prototype]]` 可能无法正确绑定——但在实践中应该很少发生。 +> 如果构造函数返回非原始值,则该值将成为 `new` 表达式的结果。在这种情况下,可能无法正确绑定 `[[Prototype]]`——但在实践中应该很少发生。 -上面的构造函数可以重写为[类](/zh-CN/docs/Web/JavaScript/Reference/Classes): +用[类](/zh-CN/docs/Web/JavaScript/Reference/Classes)将上面的构造函数重写为: ```js class Box { @@ -215,7 +215,7 @@ box.getValue(); // 2 有个推论是:_重新赋值_ `Constructor.prototype`(`Constructor.prototype = ...`)是一个不好的主意,原因有两点: -- 在重新赋值之前创建的实例的 `[[Prototype]]` 现在引用的是与重新赋值之后创建的实例的 `[[Prototype]]` 不同的对象——改变一个的 `[[Prototype]]` 不再改变另一个的 `[[Prototype]]`。 +- 在重新赋值之前创建的实例的 `[[Prototype]]` 引用的对象与重新赋值之后创建的实例的 `[[Prototype]]` 引用的对象现在是不同的——改变一个的 `[[Prototype]]` 不再改变另一个的 `[[Prototype]]`。 - 除非你手动重新设置 `constructor` 属性,否则无法再通过 `instance.constructor` 追踪到构造函数,这可能会破坏用户期望的行为。一些内置操作也会读取 `constructor` 属性,如果没有设置,它们可能无法按预期工作。 `Constructor.prototype` 仅在构造实例时有用。它与 `Constructor.[[Prototype]]` 无关,后者是构造函数的*自有*原型,即 `Function.prototype`。也就是说,`Object.getPrototypeOf(Constructor) === Function.prototype`。 @@ -385,7 +385,7 @@ console.log(doSomething.prototype); ```js function doSomething() {} -doSomething.prototype.foo = "bar"; // 向原型上添加一个属性 +doSomething.prototype.foo = "bar"; // 向原型添加一个属性 const doSomeInstancing = new doSomething(); doSomeInstancing.prop = "some value"; // 向对象添加一个属性 console.log(doSomeInstancing); @@ -470,9 +470,8 @@ function f() { // f ---> Function.prototype ---> Object.prototype ---> null const p = { b: 2, __proto__: o }; -// 可以通过 __proto__ 字面量属性将新创建对象的 +// 可以通过 __proto__ 字面量属性(不要将其与 Object.prototype.__proto__ 访问器弄混)将新创建的对象的 // [[Prototype]] 指向另一个对象。 -// (不要与 Object.prototype.__proto__ 访问器混淆) // p ---> o ---> Object.prototype ---> null ``` @@ -499,7 +498,7 @@ const g = new Graph(); ### 使用 Object.create() -调用 {{jsxref("Object.create()")}} 来创建一个新对象。该对象的 `[[Prototype]]` 是该函数的第一个参数: +调用 {{jsxref("Object.create()")}} 会创建一个新对象。该对象的 `[[Prototype]]` 是该函数的第一个参数: ```js const a = { a: 1 }; @@ -564,9 +563,9 @@ Object.setPrototypeOf(obj, anotherObj); ### 使用 \_\_proto\_\_ 访问器 -所有对象都继承了 [`Object.prototype.__proto__`](/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Object/proto) setter,它可以用来设置现有对象的 `[[Prototype]]`(如果对象没有覆盖 `__proto__` 属性)。 +所有对象都继承了 [`Object.prototype.__proto__`](/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Object/proto) setter,它可以用来设置现有对象的 `[[Prototype]]`(如果对象的 `__proto__` 属性没有被覆盖)。 -> **警告:** `Object.prototype.__proto__` 访问器是**非标准**的,且已被弃用。你应该几乎总是使用 `Object.setPrototypeOf` 作为代替。 +> **警告:** `Object.prototype.__proto__` 访问器是**非标准**的,且已被弃用。你应该几乎总是使用 `Object.setPrototypeOf`。 ```js const obj = {}; @@ -577,13 +576,13 @@ console.log(obj.fooProp); console.log(obj.barProp); ``` -与 `Object.setPrototypeOf` 相比,将 `__proto__` 设置为非对象的值只会被忽略,而非抛出异常。它也只有稍微好一点的浏览器支持。然而,它是非标准的,并且已废弃。你应该几乎总是使用 `Object.setPrototypeOf` 作为代替。 +与 `Object.setPrototypeOf` 相比,将 `__proto__` 设置为非对象的值只会被忽略,而非抛出异常。它也只有稍微好一点的浏览器支持。然而,它是非标准的,并且已被废弃。你应该几乎总是使用 `Object.setPrototypeOf`。 ## 性能 -原型链较上层的属性的查找时间可能会对性能产生负面影响,这在性能至关重要的代码中可能会非常明显。此外,尝试访问不存在的属性始终会遍历整个原型链。 +属性位于原型链上层可能会对属性的查找时间产生性能方面的负面影响,这在性能至关重要的代码中可能会非常明显。此外,尝试访问不存在的属性始终会遍历整个原型链。 -此外,在遍历对象的属性时,原型链中的**每个**可枚举属性都将被枚举。要检查对象是否具有在其*自身*上定义的属性,而不是在其原型链上的某个地方,则有必要使用 [`hasOwnProperty`](/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Object/hasOwnProperty) 或 [`Object.hasOwn`](/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Object/hasOwn) 方法。除 `[[Prototype]]` 为 `null` 的对象外,所有对象都从 `Object.prototype` 继承 [`hasOwnProperty`](/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Object/hasOwnProperty)——除非它已经在原型链较下层处被覆盖。我们将使用上面的图示例代码来说明它,具体如下: +此外,在遍历对象的属性时,原型链中的**每个**可枚举属性都将被枚举。要检查对象是否具有在其*自身*上、而不是在其原型链上的某个地方定义的属性,则有必要使用 [`hasOwnProperty`](/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Object/hasOwnProperty) 或 [`Object.hasOwn`](/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Object/hasOwn) 方法。除 `[[Prototype]]` 为 `null` 的对象外,所有对象都从 `Object.prototype` 继承 [`hasOwnProperty`](/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Object/hasOwnProperty)——除非它已经在原型链较下层处被覆盖。我们将使用上面的图示例代码来说明它,具体如下: ```js function Graph() { @@ -614,9 +613,9 @@ Object.getPrototypeOf(g).hasOwnProperty("addVertex"); // true ## 总结 -对于 Java 或 C++ 的开发者来说,JavaScript 可能有点令人困惑,因为它是完全动态、完全是在执行期间确定的,而且根本没有静态类型。一切都是对象(实例)或函数(构造函数),甚至函数本身也是 `Function` 构造函数的实例。即使是语法结构中的“类”也只是运行时的构造函数。 +对于 Java 或 C++ 的开发者来说,JavaScript 可能有点令人困惑,因为它是完全动态、完全是在执行期间确定的,而且根本没有静态类型。一切要么是对象(实例),要么是函数(构造函数),甚至函数本身也是 `Function` 构造函数的实例。即使作为语法结构的“类”在运行时也只是构造函数。 -JavaScript 中的所有构造函数都有一个被称为 `prototype` 的特殊属性,它与 `new` 运算符一起使用。对原型对象的引用被复制到新实例的内部属性 `[[Prototype]]` 中。例如,当你执行 `const a1 = new A()` 时,JavaScript(在内存中创建对象之后,定义 `this` 为该对象并执行 `A()` 之前)设置 `a1.[[Prototype]] = A.prototype`。然后,当你访问实例的属性时,JavaScript 首先检查它们是否直接存在于该对象上,如果不存在,则在 `[[Prototype]]` 中查找。会*递归*查询 `[[Prototype]]`,即 `a1.doSomething`、`Object.getPrototypeOf(a1).doSomething`、`Object.getPrototypeOf(Object.getPrototypeOf(a1)).doSomething`,以此类推,直至找到或 `Object.getPrototypeOf` 返回 `null`。这意味着在 `prototype` 上定义的所有属性实际上都由所有实例共享,并且甚至可以更改 `prototype` 的部分内容,使得更改被应用到所有现有的实例中。 +JavaScript 中的所有构造函数都有一个被称为 `prototype` 的特殊属性,它与 `new` 运算符一起使用。对原型对象的引用被复制到新实例的内部属性 `[[Prototype]]` 中。例如,当你执行 `const a1 = new A()` 时,JavaScript(在内存中创建对象之后,定义 `this` 为该对象并执行 `A()` 之前)设置 `a1.[[Prototype]] = A.prototype`。然后,当你访问实例的属性时,JavaScript 首先检查它们是否直接存在于该对象上,如果不存在,则在 `[[Prototype]]` 中查找。会*递归地*查询 `[[Prototype]]`,即 `a1.doSomething`、`Object.getPrototypeOf(a1).doSomething`、`Object.getPrototypeOf(Object.getPrototypeOf(a1)).doSomething`,依此类推,直到被找到或 `Object.getPrototypeOf` 返回 `null`。这意味着在 `prototype` 上定义的所有属性实际上都由所有实例共享,并且甚至可以更改 `prototype` 的部分内容,使得更改被应用到所有现有的实例中。 在上面的示例中,如果你执行 `const a1 = new A(); const a2 = new A();`,那么 `a1.doSomething` 实际上会引用 `Object.getPrototypeOf(a1).doSomething`——这与你定义的 `A.prototype.doSomething` 相同,即 `Object.getPrototypeOf(a1).doSomething === Object.getPrototypeOf(a2).doSomething === A.prototype.doSomething`。 From c740eb4af9dd03ac14986ae8d0d2ecb8ce80a028 Mon Sep 17 00:00:00 2001 From: familyboat <2015301110129@whu.edu.cn> Date: Wed, 6 Nov 2024 00:12:02 +0800 Subject: [PATCH 15/16] =?UTF-8?q?=E4=BD=BF=E7=94=A8=E8=A7=84=E8=8C=83?= =?UTF-8?q?=E7=94=A8=E8=AF=AD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../web/javascript/inheritance_and_the_prototype_chain/index.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/files/zh-cn/web/javascript/inheritance_and_the_prototype_chain/index.md b/files/zh-cn/web/javascript/inheritance_and_the_prototype_chain/index.md index 15d17ddaa757da..0a0b9019810869 100644 --- a/files/zh-cn/web/javascript/inheritance_and_the_prototype_chain/index.md +++ b/files/zh-cn/web/javascript/inheritance_and_the_prototype_chain/index.md @@ -576,7 +576,7 @@ console.log(obj.fooProp); console.log(obj.barProp); ``` -与 `Object.setPrototypeOf` 相比,将 `__proto__` 设置为非对象的值只会被忽略,而非抛出异常。它也只有稍微好一点的浏览器支持。然而,它是非标准的,并且已被废弃。你应该几乎总是使用 `Object.setPrototypeOf`。 +与 `Object.setPrototypeOf` 相比,将 `__proto__` 设置为非对象的值只会被忽略,而非抛出异常。它也只有稍微好一点的浏览器支持。然而,它是非标准的,并且已被弃用。你应该几乎总是使用 `Object.setPrototypeOf`。 ## 性能 From a4f967c28ea6fd6eb0708de11ab0d2978411b8d3 Mon Sep 17 00:00:00 2001 From: familyboat <84062528+familyboat@users.noreply.github.com> Date: Sun, 10 Nov 2024 10:26:44 +0800 Subject: [PATCH 16/16] Update index.md --- .../web/javascript/inheritance_and_the_prototype_chain/index.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/files/zh-cn/web/javascript/inheritance_and_the_prototype_chain/index.md b/files/zh-cn/web/javascript/inheritance_and_the_prototype_chain/index.md index 0a0b9019810869..c97cb4d643af14 100644 --- a/files/zh-cn/web/javascript/inheritance_and_the_prototype_chain/index.md +++ b/files/zh-cn/web/javascript/inheritance_and_the_prototype_chain/index.md @@ -72,7 +72,7 @@ console.log(o.d); // undefined 给对象设置属性会创建自有属性。获取和设置行为规则的唯一例外是当它被 [getter 或 setter](/zh-CN/docs/Web/JavaScript/Guide/Working_with_objects#定义_getter_与_setter) 拦截时。 -同理,你可以创建更长的原型链,并在所有的原型上查找属性。 +同理,你可以创建更长的原型链,并在所有的原型链上查找属性。 ```js const o = {