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 .prototype → Object .prototype → null
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 的实例
7127console .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// 自定义函数和对象
13133function A () {}
@@ -17,10 +137,13 @@ const a = new A();
17137console .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);
79202return newFoo; // 如果 `Foo` 构造函数没有显式返回一个对象,则返回 `newFoo`。
80203``` -->
81204
82- ## 实例化对象步骤
83- 实例化 ` const newFoo = new Foo(); ` 时的步骤
205+ ## 实例化对象步骤(new 关键字的执行过程)
206+ 实例化 ` const newFoo = new Foo(); ` 的步骤
84207``` javascript
85208function Foo () {}
86209const 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
113243newFoo .__proto__ === Foo .prototype // ✅ `newFoo` 的原型是 `Foo.prototype`
114244Foo .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
122252newFoo .constructor === Foo .prototype .constructor // ✅ `newFoo` 的构造函数是 `Foo`
123253Foo .prototype .constructor === Foo // ✅ `Foo.prototype` 的 `constructor` 指向 `Foo` 本身
124254Foo .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
131264Foo .prototype .__proto__ .constructor .__proto__ === Function .prototype // ✅ `Object` 构造函数的 `__proto__` 指向 `Function.prototype`
132265Function .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
140273Foo .prototype .constructor .prototype .constructor === Foo // ✅ 循环指向 `Foo`
141274Foo .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
149282Object .prototype .constructor === Object // ✅ `Object.prototype` 的 `constructor` 是 `Object`
150283Object .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