Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,20 +1,17 @@

Answers:
答案:

1. `true`。

赋值操作 `Rabbit.prototype` 为新对象设置了 `[[Prototype]]`,但它不影响现有的对象
`Rabbit.prototype` 的赋值操作为新对象设置了 `[[Prototype]]`,但它不影响已有的对象

2. `false`。
对象通过引用被赋值。来自 `Rabbit.prototype` 的对象并没有被复制,它仍然是被 `Rabbit.prototype` 和 `rabbit` 的 `[[Prototype]]` 引用的单个对象。

对象通过引用进行赋值。来自 `Rabbit.prototype` 的对象没有被复制,它仍然是由 `Rabbit.prototype` 和 `rabbit` 的 `[[Prototype]]` 引用的单个对象。

所以当我们通过一个引用来改变它的上下文时,它对其他引用来说是可见的。
所以当我们通过一个引用更改其内容时,它对其他引用也是可见的。

3. `true`。

所有 `delete` 操作都直接应用于对象。这里 `delete rabbit.eats` 试图从 `rabbit` 中删除 `eats` 属性,但 `rabbit` 对象并没有 `eats` 属性。所以这个操作不会有任何 副作用。
所有 `delete` 操作都直接应用于对象。这里的 `delete rabbit.eats` 试图从 `rabbit` 中删除 `eats` 属性,但 `rabbit` 对象并没有 `eats` 属性。所以这个操作不会有任何影响。

4. `undefined`。

属性 `eats` 从原型中删除,它不再存在。
属性 `eats` 被从 prototype 中删除,prototype 中就没有这个属性了。
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,11 @@ importance: 5

---

# Changing "prototype"
# 修改 "prototype"

在下面的代码中,我们创建了 `new Rabbit`,然后尝试修改其原型
在下面的代码中,我们创建了 `new Rabbit`,然后尝试修改它的 prototype

一开始,我们有这样的代码
最初,我们有以下代码

```js run
function Rabbit() {}
Expand All @@ -20,8 +20,7 @@ alert( rabbit.eats ); // true
```


1. 我们增加了一个字符串(强调),`alert` 现在会显示什么?

1. 我们增加了一个字符串(强调)。现在 `alert` 会显示什么?
```js
function Rabbit() {}
Rabbit.prototype = {
Expand All @@ -37,8 +36,7 @@ alert( rabbit.eats ); // true
alert( rabbit.eats ); // ?
```

2. ...如果代码是这样的(换了一行)?

2. ……如果代码是这样的(修改了一行)?
```js
function Rabbit() {}
Rabbit.prototype = {
Expand All @@ -54,8 +52,7 @@ alert( rabbit.eats ); // true
alert( rabbit.eats ); // ?
```

3. 像这样呢(换了一行)?

3. 像这样呢(修改了一行)?
```js
function Rabbit() {}
Rabbit.prototype = {
Expand All @@ -71,8 +68,7 @@ alert( rabbit.eats ); // true
alert( rabbit.eats ); // ?
```

4. 最后一种情况:

4. 最后一种变体:
```js
function Rabbit() {}
Rabbit.prototype = {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
如果我们确信 `"constructor"` 属性具有正确的值,我们可以使用这种方法
如果我们确信 `"constructor"` 属性具有正确的值,那么就可以使用这种方法

例如,如果我们不访问默认的 `"prototype"`,那么这段代码肯定会起作用
例如,如果我们不触碰默认的 `"prototype"`,那么这段代码肯定可以正常运行

```js run
function User(name) {
Expand All @@ -15,7 +15,7 @@ alert( user2.name ); // Pete (worked!)

它起作用了,因为 `User.prototype.constructor == User`。

...但是如果有人说,覆盖 `User.prototype` 并忘记重新创建 `"constructor"`,那么它就会失败
……但是如果有人,重写了 `User.prototype`,并忘记可重新创建 `constructor` 以引用 `User`,那么上面这段代码就会运行失败

例如:

Expand All @@ -35,10 +35,10 @@ alert( user2.name ); // undefined

为什么 `user2.name` 是 `undefined`?

`new user.constructor('Pete')` 的工作原理是
这是 `new user.constructor('Pete')` 的工作流程

1. 首先,它在 `user` 中寻找 `constructor`。什么也没有
1. 首先,它在 `user` 中寻找 `constructor`。没找到
2. 然后它追溯原型链。`user` 的原型是 `User.prototype`,它也什么都没有。
3. `User.prototype` 的值是一个普通对象 `{}`,其原型是 `Object.prototype`。还有 `Object.prototype.constructor == Object`。所以就用它了。
3. `User.prototype` 的值是一个普通对象 `{}`,该对象的原型是 `Object.prototype`。并且 `Object.prototype.constructor == Object`。所以就用它了。

最后,我们有 `let user2 = new Object('Pete')`。内置的 `Object` 构造函数忽略参数,它总是创建一个空对象 —— 这就是我们在 `user2` 中所拥有的东西
最后,我们有 `let user2 = new Object('Pete')`。内建的 `Object` 构造函数会忽略参数,它总是创建一个类似于 `let user2 = {}` 的空对象,这就是最后我们在 `user2` 中拥有的东西
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,12 @@ importance: 5

# 使用相同的构造函数创建一个对象

想象一下,我们有一个任意对象 `obj`,由一个构造函数创建 —— 我们不知道这个构造函数是什么,但是我们想用它创建一个新对象
想象一下,我们有一个由构造函数创建的对象 `obj` —— 我们不知道使用的是哪个构造函数,但是我们想使用它创建一个新对象

我们可以这样做吗?

```js
let obj2 = new obj.constructor();
```

给出一个代码可以正常工作的 `obj` 的构造函数的例子。再给出一个会导致错误的例子
请给出一个可以使这样的代码正常工作的 `obj` 的构造函数的例子。再给出会导致这样的代码无法正确工作的例子
70 changes: 35 additions & 35 deletions 1-js/08-prototypes/02-function-prototype/article.md
Original file line number Diff line number Diff line change
@@ -1,18 +1,18 @@
# 函数原型
# F.prototype

正如我们已经知道的那样,`new F()` 创建一个新对象
我们还记得,可以使用诸如 `new F()` 这样的构造函数来创建一个新对象

当用 `new F()` 创建一个新对象时,该对象的 `[[Prototype]]` 被设置为 `F.prototype`。
如果 `F.prototype` 是一个对象,那么 `new` 操作符会使用它为新对象设置 `[[Prototype]]`。

```smart
JavaScript 从一开始就有了原型继承。这是该语言的核心特征之一
JavaScript 从一开始就有了原型继承。这是 JavaScript 编程语言的核心特性之一

但在过去,我们是无法直接对其进行访问的。唯一可靠的设置方法是使用构造函数的“prototype属性。我们将在本章对其进行讨论。即使现在,还是有很多使用它的脚本
但是在过去,没有直接对其进行访问的方式。唯一可靠的方法是本章中会介绍的构造函数的 `"prototype"` 属性。目前仍有许多脚本仍在使用它
```

请注意,`F.prototype` 意味着在 `F` 上有一个名为 `"prototype"` 的常规属性。这听起来与“原型”这个术语很相似,但在这里我们的意思是指有这个名字的常规属性
请注意,这里的 `F.prototype` 指的是 `F` 的一个名为 `"prototype"` 的常规属性。这听起来与“原型”这个术语很类似,但这里我们实际上指的是具有该名字的常规属性

这是一个例子
下面是一个例子

```js run
let animal = {
Expand All @@ -32,25 +32,25 @@ let rabbit = new Rabbit("White Rabbit"); // rabbit.__proto__ == animal
alert( rabbit.eats ); // true
```

设置 `Rabbit.prototype = animal` 的这段代码表达的意思是:“当 `new Rabbit` 创建时,把它的 `[[Prototype]]` 赋值为 `animal`”
设置 `Rabbit.prototype = animal` 的字面意思是:“当创建了一个 `new Rabbit` ,把它的 `[[Prototype]]` 赋值为 `animal`”。

这是结果图
这是结果示意图

![](proto-constructor-animal-rabbit.svg)

在图片上,`"prototype"` 是一个水平箭头,它是一个常规属性,`[[Prototype]]` 是垂直的,意味着是继承自 `animal` 的 `rabbit`。
在上图中,`"prototype"` 是一个水平箭头,表示一个常规属性,`[[Prototype]]` 是垂直的,表示 `rabbit` 继承自 `animal`。

```smart header="`F.prototype`仅用于`new F`时"
`F.prototype` 属性仅在 `new F` 调用时使用,它为新对象的 `[[Prototype]]` 赋值。在此之后,`F.prototype` 和新对象之间就分道扬镳了。可以将其看为一个“单次赠与”效果
```smart header="`F.prototype` 仅用在 `new F` 时"
`F.prototype` 属性仅在 `new F` 被调用时使用,它为新对象的 `[[Prototype]]` 赋值。之后,`F.prototype` 和新对象之间就没有任何联系了。可以把它看成“一次性的礼物”

如果在创建之后 `F.prototype` 属性有了变化(`F.prototype = <another object>`),那么 `new F` 创建的新对象也将随之拥有新的 `[[Prototype]]`但已经存在的对象将保持旧有的值。
如果在创建之后`F.prototype` 属性有了变化`F.prototype = <another object>`),那么通过 `new F` 创建的新对象也将随之拥有新的对象作为 `[[Prototype]]`但已经存在的对象将保持旧有的值。
```

## 默认的函数原型,构造函数属性
## 默认的 F.prototype,构造函数属性

每个函数都有 `"prototype"` 属性,即使我们不设置它
每个函数都有 `"prototype"` 属性,即使我们没有提供它

默认的 `"prototype"` 是一个只有属性 `constructor` 的对象,它指向函数本身
默认的 `"prototype"` 是一个只有属性 `constructor` 的对象,属性 `constructor` 指向函数自身

像这样:

Expand All @@ -74,7 +74,7 @@ function Rabbit() {}
alert( Rabbit.prototype.constructor == Rabbit ); // true
```

当然,如果我们什么都不做,`constructor` 属性可以通过 `[[Prototype]]` 给所有 rabbits 对象使用
通常,如果我们什么都不做,`constructor` 属性可以通过 `[[Prototype]]` 给所有 rabbits 使用

```js run
function Rabbit() {}
Expand All @@ -88,9 +88,10 @@ alert(rabbit.constructor == Rabbit); // true (from prototype)

![](rabbit-prototype-constructor.svg)

我们可以用 `constructor` 属性使用与现有构造函数相同的构造函数创建一个新对象
我们可以使用 `constructor` 属性来创建一个新对象,该对象使用与现有对象相同的构造函数

像这样:

```js run
function Rabbit(name) {
this.name = name;
Expand All @@ -104,16 +105,15 @@ let rabbit2 = new rabbit.constructor("Black Rabbit");
*/!*
```

当我们有一个对象,但不知道为它使用哪个构造函数(比如它来自第三方库),而且我们需要创建另一个相似的函数时,用这种方法就很方便。

当我们有一个对象,但不知道它使用了哪个构造函数(例如它来自第三方库),并且我们需要创建另一个类似的对象时,用这种方法就很方便。

但关于 `"constructor"` 最重要的是......
但是,关于 `"constructor"` 最重要的是……

**...JavaScript 本身并不能确保正确的 `"constructor"` 函数值。**
**……JavaScript 自身并不能确保正确的 `"constructor"` 函数值。**

是的,它存在于函数的默认 `"prototype"` 中,但仅此而已。之后会发生什么 —— 完全取决于我们自己
是的,它存在于函数的默认 `"prototype"` 中,但仅此而已。之后会发生什么 —— 完全取决于我们

特别是,如果我们将整个默认原型替换掉,那么其中就不会有构造函数
特别是,如果我们将整个默认 prototype 替换掉,那么其中就不会有 `"constructor"` 了

例如:

Expand All @@ -129,15 +129,15 @@ alert(rabbit.constructor === Rabbit); // false
*/!*
```

因此,为了确保正确的 `"constructor"`,我们可以选择添加/删除属性到默认 `"prototype"` 而不是将其整个覆盖:
因此,为了确保正确的 `"constructor"`,我们可以选择添加/删除属性到默认 `"prototype"`而不是将其整个覆盖:

```js
function Rabbit() {}

// Not overwrite Rabbit.prototype totally
// just add to it
// 不要将 Rabbit.prototype 整个覆盖
// 可以向其中添加内容
Rabbit.prototype.jumps = true
// the default Rabbit.prototype.constructor is preserved
// 默认的 Rabbit.prototype.constructor 被保留了下来
```

或者,也可以手动重新创建 `constructor` 属性:
Expand All @@ -156,20 +156,20 @@ Rabbit.prototype = {

## 总结

在本章中,我们简要介绍了如何为通过构造函数创建的对象设置一个 `[[Prototype]]`。稍后我们将看到更多依赖于它的高级编程模式
在本章中,我们简要介绍了为通过构造函数创建的对象设置 `[[Prototype]]` 的方法。稍后我们将看到更多依赖于此的高级编程模式

一切都很简单,只需要几条笔记就能说清楚
一切都很简单,只需要记住几条重点就可以清晰地掌握了

- `F.prototype` 属性与 `[[Prototype]]` 不同。`F.prototype` 唯一的作用是:当 `new F()` 被调用时,它设置新对象的 `[[Prototype]]`。
- `F.prototype` 的值应该是一个对象或 null:其他值将不起作用
- `"prototype"` 属性在设置为构造函数时仅具有这种特殊效果,并且用 `new` 调用
- `F.prototype` 属性(不要把它与 `[[Prototype]]` 弄混了)在 `new F` 被调用时为新对象的 `[[Prototype]]` 赋值
- `F.prototype` 的值要么是一个对象,要么就是 `null`:其他值都不起作用
- `"prototype"` 属性仅在设置了一个构造函数(constructor function),并通过 `new` 调用时,才具有这种特殊的影响

在常规对象上,`prototype` 没什么特别的:
```js
let user = {
name: "John",
prototype: "Bla-bla" // 没什么神秘的
prototype: "Bla-bla" // 这里没有魔法了
};
```

默认情况下,所有函数都有 `F.prototype = {constructor:F}`,所以我们可以通过访问它的 `"constructor"` 属性来获得对象的构造函数
默认情况下,所有函数都有 `F.prototype = {constructor:F}`,所以我们可以通过访问它的 `"constructor"` 属性来获取一个对象的构造函数
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading