Skip to content

Commit 74c3dc1

Browse files
committed
update prototype
1 parent 05c7f6d commit 74c3dc1

File tree

1 file changed

+184
-23
lines changed

1 file changed

+184
-23
lines changed

programming-paradigm/object-oriented-programming/js_prototype.md

Lines changed: 184 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,133 @@
11

2-
# JS prototype 详解
2+
# JavaScript prototype 详解
3+
4+
JavaScript 中,**prototype(原型)** 是面向对象编程的核心概念之一。它通过 **原型链(Prototype Chain)** 实现继承,使对象可以共享其他对象的属性和方法。理解原型机制是掌握 JavaScript 面向对象编程的关键。
5+
6+
---
7+
8+
## 什么是 prototype?
9+
每个 JavaScript **函数**(构造函数)都有一个 `prototype` 属性,它是一个对象。所有由该函数创建的 **实例对象** 都会继承这个原型对象的属性和方法。
10+
11+
```js
12+
function Person(name) {
13+
this.name = name;
14+
}
15+
16+
// 方法添加到原型,所有实例共享
17+
Person.prototype.sayHello = function() {
18+
console.log(`Hello, my name is ${this.name}`);
19+
};
20+
21+
const p1 = new Person("Joe");
22+
const p2 = new Person("Mary");
23+
24+
p1.sayHello(); // Hello, my name is Joe
25+
p2.sayHello(); // Hello, my name is Mary
26+
27+
// 实例的 __proto__ 指向构造函数的 prototype
28+
console.log(p1.__proto__ === Person.prototype); // true
29+
```
30+
**在上面的代码中:**
31+
- `Person.prototype``Person` 构造函数的原型对象。
32+
- `sayHello` 方法被所有 `Person` 的实例共享,而不是每个实例都创建一份新的拷贝,节省内存。
33+
- `p1.__proto__` 指向 `Person.prototype`,表示 p1 继承了 `Person.prototype` 上的方法。
34+
- `__proto__` 是实例对象的隐式原型引用(非标准属性,可以用 `Object.getPrototypeOf()` 替代)。
35+
36+
---
37+
38+
## 属性`__proto__` 与 prototype 关系
39+
40+
JavaScript 中每个对象都有一个隐藏的 `__proto__` 属性(这个并非标准属性,虽然大部分浏览器都支持),它指向创建该对象的构造函数的 prototype:
41+
42+
```js
43+
console.log(p1.__proto__ === Person.prototype); // true
44+
console.log(Person.prototype.__proto__ === Object.prototype); // true
45+
console.log(Object.prototype.__proto__ === null); // true
46+
```
47+
这个原型链的结构如下:
48+
```js
49+
// 访问对象属性时,若当前对象没有,则沿原型链向上查找。
50+
// Object.prototype 是原型链的终点,其 __proto__ 为 null。
51+
p1 → Person.prototypeObject.prototypenull
52+
```
53+
54+
## 原型链继承
55+
可以通过 `prototype` 让一个构造函数继承另一个构造函数的方法和属性。
56+
注意,使用 Object.create 创建的子对象不会调用父构造函数,仅用于设置原型:
57+
```js
58+
function Parent(name) {
59+
this.name = name;
60+
}
61+
62+
Parent.prototype.makeSound = function() {
63+
console.log("Parent are saying.");
64+
};
65+
66+
function Child(name, age) {
67+
Parent.call(this, name); // 继承属性
68+
this.age = age;
69+
}
70+
71+
// 使用 Object.create 创建新的原型对象,让 Child 继承 Parent 的方法
72+
Child.prototype = Object.create(Parent.prototype);
73+
// 修正 constructor 指向,否则 Child.prototype.constructor 会指向 Parent
74+
Child.prototype.constructor = Child;
75+
76+
Child.prototype.speak = function() {
77+
console.log("Child is talking.");
78+
};
79+
80+
const d = new Child("Child1", 18);
81+
d.makeSound(); // Parent are saying.
82+
d.speak(); // Child is talking.
83+
```
84+
**步骤分析:**
85+
- **Object.create(Parent.prototype)**
86+
创建一个新对象,其原型指向 `Parent.prototype`,确保子类原型不污染父类。
87+
88+
- **修复 constructor 指向**
89+
若不修复,`Child.prototype.constructor` 将指向 `Parent`,导致实例的 `constructor` 错误。
90+
91+
- **构造函数借用 (Parent.call)**
92+
在子类构造函数中调用父类构造函数,初始化实例属性。
93+
94+
## ES6 class 语法的 prototype
95+
ES6 的 class 是原型的语法糖,本质仍基于原型链:
96+
```js
97+
class Person {
98+
constructor(name) {
99+
this.name = name;
100+
}
101+
102+
// 方法自动添加到 Person.prototype
103+
sayHello() {
104+
console.log(`Hello, my name is ${this.name}`);
105+
}
106+
}
107+
108+
const p = new Person("Tom");
109+
p.sayHello(); // Hello, my name is Tom
110+
111+
// 静态方法添加到构造函数本身
112+
Person.staticMethod = function() {
113+
console.log("This is a static method.");
114+
};
115+
116+
console.log(Object.getPrototypeOf(p) === Person.prototype); // true
117+
```
118+
在这个例子中:
119+
- `sayHello` 方法实际存储在 `Person.prototype`
120+
- `static` 关键字定义的方法属于构造函数本身,而非原型。
121+
3122

4123
## prototype验证
124+
**构造函数与原型的关系**
5125
```js
6126
// 验证 Object 是 Function 的实例
7127
console.log(Object instanceof Function); // 输出: true
8128

9129
// 验证 Function 继承自 Object.prototype
10-
console.log(Function.prototype.__proto__ === Object.prototype); // 输出: true
130+
console.log(Object.getPrototypeOf(Function.prototype) === Object.prototype); // 输出: true
11131

12132
// 自定义函数和对象
13133
function A() {}
@@ -17,10 +137,13 @@ const a = new A();
17137
console.log(A instanceof Function); // 输出: true
18138

19139
// 验证自定义对象的原型是自定义函数的 prototype
20-
console.log(a.__proto__ === A.prototype); // 输出: true
140+
console.log(Object.getPrototypeOf(a) === A.prototype); // 输出: true
141+
142+
// 验证自定义函数的 prototype 的原型是 Object.prototype
143+
console.log(Object.getPrototypeOf(A.prototype) === Object.prototype); // 输出: true
21144

22-
// 验证自定义函数的原型的原型是 Object.prototype
23-
console.log(A.prototype.__proto__ === Object.prototype); // 输出: true
145+
// 原型链的终点
146+
console.log(Object.prototype.__proto__); // null
24147
```
25148

26149
## prototype图形展示
@@ -79,8 +202,8 @@ const result = Foo.apply(newFoo, args);
79202
return newFoo; // 如果 `Foo` 构造函数没有显式返回一个对象,则返回 `newFoo`。
80203
``` -->
81204

82-
## 实例化对象步骤
83-
实例化 `const newFoo = new Foo();` 时的步骤
205+
## 实例化对象步骤(new 关键字的执行过程)
206+
实例化 `const newFoo = new Foo();` 的步骤
84207
```javascript
85208
function Foo() {}
86209
const newFoo = new Foo();
@@ -99,16 +222,23 @@ newFoo.__proto__ = Foo.prototype;
99222
```
100223

101224
### **3. 执行构造函数,并绑定 `this`**
225+
```js
226+
const result = Foo.apply(newFoo, arguments);
227+
```
102228
调用 `Foo` 构造函数,并将 `newFoo` 作为 `this` 传入。
103229
`Foo` 显式返回一个对象,则 `new` 操作符返回该对象;否则返回 `newFoo`
230+
231+
### 返回对象
232+
若构造函数返回对象,则返回该对象。否则返回新创建的 obj。
104233
```javascript
105-
const result = Foo.apply(newFoo, args);
106-
return typeof result === "object" ? result : newFoo;
234+
return typeof result === "object" && result !== null ? result : newFoo;
107235
```
108236

109237
---
110238

111-
# **原型链分析**
239+
基于上面 `newFoo = new Foo();` 进行分析。
240+
241+
## **原型链分析**
112242
```javascript
113243
newFoo.__proto__ === Foo.prototype // ✅ `newFoo` 的原型是 `Foo.prototype`
114244
Foo.prototype.__proto__ === Object.prototype // ✅ `Foo.prototype` 的原型是 `Object.prototype`
@@ -117,16 +247,19 @@ Object.prototype.__proto__ === null // ✅ `Object.prototype` 的原型是 `nu
117247

118248
---
119249

120-
# **构造器关系**
250+
## **构造器关系**
121251
```javascript
122252
newFoo.constructor === Foo.prototype.constructor // ✅ `newFoo` 的构造函数是 `Foo`
123253
Foo.prototype.constructor === Foo // ✅ `Foo.prototype` 的 `constructor` 指向 `Foo` 本身
124254
Foo.prototype.constructor.prototype === Foo.prototype // ✅ `Foo.prototype.constructor` 的 `prototype` 仍然是 `Foo.prototype`
125255
```
256+
**说明:**
257+
- 当我们创建一个新对象时,它的 `constructor` 属性通常来源于它的原型(即 `Foo.prototype.constructor`)。
258+
- 使用 `Object.create` 或修改原型时,有可能需要手动修正 `constructor` 指向。
126259

127260
---
128261

129-
# **`Function``Object` 互相指向**
262+
## **`Function``Object` 互相指向**
130263
```javascript
131264
Foo.prototype.__proto__.constructor.__proto__ === Function.prototype // ✅ `Object` 构造函数的 `__proto__` 指向 `Function.prototype`
132265
Function.prototype === Object.__proto__ // ✅ `Function.prototype` 就是 `Object` 的 `__proto__`
@@ -135,7 +268,7 @@ Function.prototype.__proto__.__proto__ === null // ✅ `Function.prototype.__p
135268

136269
---
137270

138-
# **构造器和原型链的循环指向**
271+
## **构造器和原型链的循环指向**
139272
```javascript
140273
Foo.prototype.constructor.prototype.constructor === Foo // ✅ 循环指向 `Foo`
141274
Foo.prototype.constructor.prototype.constructor.prototype === Foo.prototype // ✅ 再次循环指向 `Foo.prototype`
@@ -144,7 +277,7 @@ Foo.prototype.constructor === Foo // ✅ `Foo.prototype.constructor` 仍然指
144277

145278
---
146279

147-
# **`Object``Function` 之间的关系**
280+
## **`Object``Function` 之间的关系**
148281
```javascript
149282
Object.prototype.constructor === Object // ✅ `Object.prototype` 的 `constructor` 是 `Object`
150283
Object.prototype.constructor.__proto__ === Function.prototype // ✅ `Object` 构造函数本身是 `Function` 的一个实例
@@ -156,16 +289,44 @@ Object.prototype.__proto__ === null // ✅ `Object.prototype` 是原型链终
156289

157290
---
158291

159-
# **总结**
292+
## 原型使用的注意事项
293+
- 避免直接修改内置原型
294+
`Array.prototype.myMethod` = ... 可能导致兼容性问题。
295+
296+
- 原型属性的共享特性
297+
引用类型(如数组)的属性可能被所有实例意外修改:
298+
```js
299+
function MyClass() {}
300+
MyClass.prototype.data = [];
301+
302+
const a = new MyClass();
303+
a.data.push(1); // 所有实例的 data 都会变化
304+
```
305+
- 性能优化
306+
将方法定义在原型上,而非构造函数内,减少内存占用。
307+
308+
---
309+
310+
## **总结**
311+
- **`prototype` 属性**
312+
1. 每个 JavaScript 函数 都有一个 `prototype` 属性(除了箭头函数)。
313+
2. `prototype` 是一个对象,所有由该函数创建的实例都会共享 `prototype` 上的方法。
314+
3. `__proto__` 指向该对象的原型(即构造函数的 `prototype`),形成原型链。
315+
4. 通过 `Object.create()` 进行原型继承,ES6 `class` 语法是 `prototype` 的语法糖。
316+
5. 原型链终点 为 Object.prototype,其 __proto__ 为 null。
317+
160318
- **`new` 关键字的作用**
161-
1. 创建一个新对象 `newFoo`
162-
2. 设置 `newFoo.__proto__ = Foo.prototype`
163-
3. 执行 `Foo` 并绑定 `this`
164-
4. 返回 `newFoo` 或构造函数返回的对象
319+
1. 创建一个新对象 `newFoo`
320+
2. 设置 `newFoo.__proto__ = Foo.prototype`
321+
3. 执行 `Foo` 并绑定 `this`
322+
4. 返回 `newFoo` 或构造函数返回的对象
165323

166324
- **构造函数、原型和 `Object` 的关系**
167-
- `Foo.prototype` 继承自 `Object.prototype`
168-
- `Object.prototype` 是所有对象的原型链终点
169-
- `Object``Function` 互相指向,`Object` 也是 `Function` 的一个实例
170-
- `Function.prototype.__proto__ === Object.prototype`,最终 `Function` 也继承自 `Object`
325+
1. `Foo.prototype` 继承自 `Object.prototype`
326+
2. `Object.prototype` 是所有对象的原型链终点
327+
3. `Object``Function` 互相指向,`Object` 也是 `Function` 的一个实例
328+
4. `Function.prototype.__proto__ === Object.prototype`,最终 `Function` 也继承自 `Object`
329+
330+
更多链接:
331+
[https://github.com/microwind/design-patterns](https://github.com/microwind/design-patterns/tree/main/programming-paradigm/object-oriented-programming)
171332

0 commit comments

Comments
 (0)