一文搞清楚什么是JavaScript prototype原型链
<h1 id="javascript-prototype-详解">JavaScript prototype 详解</h1><p>JavaScript 中,<strong>prototype(原型)</strong> 是面向对象编程的核心概念之一。它通过 <strong>原型链(Prototype Chain)</strong> 实现继承,使对象可以共享其他对象的属性和方法。理解原型机制是掌握 JavaScript 面向对象编程的关键。</p>
<hr>
<h2 id="什么是-prototype">什么是 prototype?</h2>
<p>每个 JavaScript <strong>函数</strong>(构造函数)都有一个 <code>prototype</code> 属性,它是一个对象。所有由该函数创建的 <strong>实例对象</strong> 都会继承这个原型对象的属性和方法。</p>
<pre><code class="language-js">function Person(name) {
this.name = name;
}
// 方法添加到原型,所有实例共享
Person.prototype.sayHello = function() {
console.log(`Hello, my name is ${this.name}`);
};
const p1 = new Person("Joe");
const p2 = new Person("Mary");
p1.sayHello(); // Hello, my name is Joe
p2.sayHello(); // Hello, my name is Mary
// 实例的 __proto__ 指向构造函数的 prototype
console.log(p1.__proto__ === Person.prototype); // true
</code></pre>
<p><strong>在上面的代码中:</strong></p>
<ul>
<li><code>Person.prototype</code> 是 <code>Person</code> 构造函数的原型对象。</li>
<li><code>sayHello</code> 方法被所有 <code>Person</code> 的实例共享,而不是每个实例都创建一份新的拷贝,节省内存。</li>
<li><code>p1.__proto__</code> 指向 <code>Person.prototype</code>,表示 p1 继承了 <code>Person.prototype</code> 上的方法。</li>
<li><code>__proto__</code> 是实例对象的隐式原型引用(非标准属性,可以用 <code>Object.getPrototypeOf()</code> 替代)。</li>
</ul>
<hr>
<h2 id="属性__proto__-与-prototype-关系">属性<code>__proto__</code> 与 prototype 关系</h2>
<p>JavaScript 中每个对象都有一个隐藏的 <code>__proto__</code> 属性(这个并非标准属性,虽然大部分浏览器都支持),它指向创建该对象的构造函数的 prototype:</p>
<pre><code class="language-js">console.log(p1.__proto__ === Person.prototype); // true
console.log(Person.prototype.__proto__ === Object.prototype); // true
console.log(Object.prototype.__proto__ === null); // true
</code></pre>
<p>这个原型链的结构如下:</p>
<pre><code class="language-js">// 访问对象属性时,若当前对象没有,则沿原型链向上查找。
// Object.prototype 是原型链的终点,其 __proto__ 为 null。
p1 → Person.prototype → Object.prototype → null
</code></pre>
<h2 id="原型链继承">原型链继承</h2>
<p>可以通过 <code>prototype</code> 让一个构造函数继承另一个构造函数的方法和属性。<br>
注意,使用 Object.create 创建的子对象不会调用父构造函数,仅用于设置原型:</p>
<pre><code class="language-js">function Parent(name) {
this.name = name;
}
Parent.prototype.makeSound = function() {
console.log("Parent are saying.");
};
function Child(name, age) {
Parent.call(this, name); // 继承属性
this.age = age;
}
// 使用 Object.create 创建新的原型对象,让 Child 继承 Parent 的方法
Child.prototype = Object.create(Parent.prototype);
// 修正 constructor 指向,否则 Child.prototype.constructor 会指向 Parent
Child.prototype.constructor = Child;
Child.prototype.speak = function() {
console.log("Child is talking.");
};
const d = new Child("Child1", 18);
d.makeSound(); // Parent are saying.
d.speak(); // Child is talking.
</code></pre>
<p><strong>步骤分析:</strong></p>
<ul>
<li>
<p><strong>Object.create(Parent.prototype)</strong><br>
创建一个新对象,其原型指向 <code>Parent.prototype</code>,确保子类原型不污染父类。</p>
</li>
<li>
<p><strong>修复 constructor 指向</strong><br>
若不修复,<code>Child.prototype.constructor</code> 将指向 <code>Parent</code>,导致实例的 <code>constructor</code> 错误。</p>
</li>
<li>
<p><strong>构造函数借用 (Parent.call)</strong><br>
在子类构造函数中调用父类构造函数,初始化实例属性。</p>
</li>
</ul>
<h2 id="es6-class-语法的-prototype">ES6 class 语法的 prototype</h2>
<p>ES6 的 class 是原型的语法糖,本质仍基于原型链:</p>
<pre><code class="language-js">class Person {
constructor(name) {
this.name = name;
}
// 方法自动添加到 Person.prototype
sayHello() {
console.log(`Hello, my name is ${this.name}`);
}
}
const p = new Person("Tom");
p.sayHello(); // Hello, my name is Tom
// 静态方法添加到构造函数本身
Person.staticMethod = function() {
console.log("This is a static method.");
};
console.log(Object.getPrototypeOf(p) === Person.prototype); // true
</code></pre>
<p>在这个例子中:</p>
<ul>
<li><code>sayHello</code> 方法实际存储在 <code>Person.prototype</code>。</li>
<li><code>static</code> 关键字定义的方法属于构造函数本身,而非原型。</li>
</ul>
<h2 id="prototype验证">prototype验证</h2>
<p><strong>构造函数与原型的关系</strong></p>
<pre><code class="language-js">// 验证 Object 是 Function 的实例
console.log(Object instanceof Function); // 输出: true
// 验证 Function 继承自 Object.prototype
console.log(Object.getPrototypeOf(Function.prototype) === Object.prototype); // 输出: true
// 自定义函数和对象
function A() {}
const a = new A();
// 验证自定义函数是 Function 的实例
console.log(A instanceof Function); // 输出: true
// 验证自定义对象的原型是自定义函数的 prototype
console.log(Object.getPrototypeOf(a) === A.prototype); // 输出: true
// 验证自定义函数的 prototype 的原型是 Object.prototype
console.log(Object.getPrototypeOf(A.prototype) === Object.prototype); // 输出: true
// 原型链的终点
console.log(Object.prototype.__proto__); // null
</code></pre>
<h2 id="prototype图形展示">prototype图形展示</h2>
<pre><code class="language-text">// https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Inheritance_and_the_prototype_chain
+----------------+constructor+---------------------+
| Function | <------------ |Function.prototype |
| (Function 本身) | ------------> | (Function 原型) |
+----------------+ prototype +---------------------+
__proto__ ^ |
| |
|------------------------------ | __proto__
| __proto__ |
| v
+----------------+constructor +------------------+ +--------+
| Function | <----------|Object.prototype | ---------> |终点 |
| (Object 函数) | ---------->|(所有对象的基类) |__proto__ |null|
+----------------+prototype +------------------+ +--------+
^
|
| __proto__
|
+----------------+constructor +----------------------+
| function Foo() |<---------| Foo.prototype |
|(自定义函数) |----------> |(自定义函数原型对象) |
+----------------+ prototype+----------------------+
^
|
|
+----------------+ |
| new Foo() | -----------------------|
|(函数实例对象)| __proto__
+----------------+
</code></pre>
<h2 id="实例化对象步骤new-关键字的执行过程">实例化对象步骤(new 关键字的执行过程)</h2>
<p>实例化 <code>const newFoo = new Foo();</code> 的步骤</p>
<pre><code class="language-javascript">function Foo() {}
const newFoo = new Foo();
</code></pre>
<p><strong>1. 创建一个新对象</strong><br>
JavaScript 先创建一个新的空对象 <code>newFoo</code>。</p>
<pre><code class="language-javascript">const newFoo = {};
</code></pre>
<p><strong>2. 设置新对象的原型</strong><br>
<code>newFoo.__proto__</code> 被设置为 <code>Foo.prototype</code>,即 <code>newFoo</code> 继承了 <code>Foo.prototype</code> 的属性和方法。</p>
<pre><code class="language-javascript">newFoo.__proto__ = Foo.prototype;
</code></pre>
<p><strong>3. 执行构造函数,并绑定 <code>this</code></strong></p>
<pre><code class="language-js">const result = Foo.apply(newFoo, arguments);
</code></pre>
<p>调用 <code>Foo</code> 构造函数,并将 <code>newFoo</code> 作为 <code>this</code> 传入。<br>
若 <code>Foo</code> 显式返回一个对象,则 <code>new</code> 操作符返回该对象;否则返回 <code>newFoo</code>。</p>
<p><strong>4. 返回对象</strong><br>
若构造函数返回对象,则返回该对象。否则返回新创建的 obj。</p>
<pre><code class="language-javascript">return typeof result === "object" && result !== null ? result : newFoo;
</code></pre>
<hr>
<h2 id="原型链分析">原型链分析</h2>
<p>基于上面 <code>const newFoo = new Foo();</code> 进行分析。</p>
<p><strong>原型链指向</strong></p>
<pre><code class="language-javascript">newFoo.__proto__ === Foo.prototype // ✅ `newFoo` 的原型是 `Foo.prototype`
Foo.prototype.__proto__ === Object.prototype // ✅ `Foo.prototype` 的原型是 `Object.prototype`
Object.prototype.__proto__ === null // ✅ `Object.prototype` 的原型是 `null`(即原型链的终点)
</code></pre>
<p><strong>构造器关系</strong></p>
<pre><code class="language-javascript">newFoo.constructor === Foo.prototype.constructor // ✅ `newFoo` 的构造函数是 `Foo`
Foo.prototype.constructor === Foo // ✅ `Foo.prototype` 的 `constructor` 指向 `Foo` 本身
Foo.prototype.constructor.prototype === Foo.prototype // ✅ `Foo.prototype.constructor` 的 `prototype` 仍然是 `Foo.prototype`
</code></pre>
<p><strong>说明:</strong></p>
<ul>
<li>当我们创建一个新对象时,它的 <code>constructor</code> 属性通常来源于它的原型(即 <code>Foo.prototype.constructor</code>)。</li>
<li>使用 <code>Object.create</code> 或修改原型时,有可能需要手动修正 <code>constructor</code> 指向。</li>
</ul>
<p><strong><code>Function</code> 和 <code>Object</code> 互相指向</strong></p>
<pre><code class="language-javascript">Foo.prototype.__proto__.constructor.__proto__ === Function.prototype // ✅ `Object` 构造函数的 `__proto__` 指向 `Function.prototype`
Function.prototype === Object.__proto__ // ✅ `Function.prototype` 就是 `Object` 的 `__proto__`
Function.prototype.__proto__.__proto__ === null // ✅ `Function.prototype.__proto__` 是 `Object.prototype`,再往上是 `null`
</code></pre>
<p><strong>构造器和原型链的循环指向</strong></p>
<pre><code class="language-javascript">Foo.prototype.constructor.prototype.constructor === Foo // ✅ 循环指向 `Foo`
Foo.prototype.constructor.prototype.constructor.prototype === Foo.prototype // ✅ 再次循环指向 `Foo.prototype`
Foo.prototype.constructor === Foo // ✅ `Foo.prototype.constructor` 仍然指向 `Foo`
</code></pre>
<p><strong><code>Object</code> 和 <code>Function</code> 之间的关系</strong></p>
<pre><code class="language-javascript">Object.prototype.constructor === Object // ✅ `Object.prototype` 的 `constructor` 是 `Object`
Object.prototype.constructor.__proto__ === Function.prototype // ✅ `Object` 构造函数本身是 `Function` 的一个实例
Function.constructor.__proto__ === Function.prototype // ✅ `Function` 构造函数的 `__proto__` 也是 `Function.prototype`
Function.prototype.__proto__ === Object.prototype // ✅ `Function.prototype` 继承自 `Object.prototype`
Function.__proto__.__proto__ === Object.prototype // ✅ `Function.__proto__` 继承自 `Function.prototype`,最终指向 `Object.prototype`
Object.prototype.__proto__ === null // ✅ `Object.prototype` 是原型链终点
</code></pre>
<hr>
<h2 id="原型使用的注意事项">原型使用的注意事项</h2>
<ul>
<li>
<p>避免直接修改内置原型<br>
如 <code>Array.prototype.myMethod</code> = ... 可能导致兼容性问题。</p>
</li>
<li>
<p>原型属性的共享特性<br>
引用类型(如数组)的属性可能被所有实例意外修改:</p>
</li>
</ul>
<pre><code class="language-js">function MyClass() {}
MyClass.prototype.data = [];
const a = new MyClass();
a.data.push(1); // 所有实例的 data 都会变化
</code></pre>
<ul>
<li>性能优化<br>
将方法定义在原型上,而非构造函数内,减少内存占用。</li>
</ul>
<hr>
<h2 id="总结"><strong>总结</strong></h2>
<ul>
<li><strong><code>prototype</code> 属性</strong></li>
</ul>
<ol>
<li>每个 JavaScript 函数 都有一个 <code>prototype</code> 属性(除了箭头函数)。</li>
<li><code>prototype</code> 是一个对象,所有由该函数创建的实例都会共享 <code>prototype</code> 上的方法。</li>
<li><code>__proto__</code> 指向该对象的原型(即构造函数的 <code>prototype</code>),形成原型链。</li>
<li>通过 <code>Object.create()</code> 进行原型继承,ES6 <code>class</code> 语法是 <code>prototype</code> 的语法糖。</li>
<li>原型链终点 为 Object.prototype,其 <strong>proto</strong> 为 null。</li>
</ol>
<ul>
<li><strong><code>new</code> 关键字的作用</strong></li>
</ul>
<ol>
<li>创建一个新对象 <code>newFoo</code></li>
<li>设置 <code>newFoo.__proto__ = Foo.prototype</code></li>
<li>执行 <code>Foo</code> 并绑定 <code>this</code></li>
<li>返回 <code>newFoo</code> 或构造函数返回的对象</li>
</ol>
<ul>
<li><strong>构造函数、原型和 <code>Object</code> 的关系</strong></li>
</ul>
<ol>
<li><code>Foo.prototype</code> 继承自 <code>Object.prototype</code></li>
<li><code>Object.prototype</code> 是所有对象的原型链终点</li>
<li><code>Object</code> 和 <code>Function</code> 互相指向,<code>Object</code> 也是 <code>Function</code> 的一个实例</li>
<li><code>Function.prototype.__proto__ === Object.prototype</code>,最终 <code>Function</code> 也继承自 <code>Object</code></li>
</ol>
<p>更多链接:<br>
https://github.com/microwind/design-patterns</p><br><br>
来源:https://www.cnblogs.com/jarryli/p/18795446
頁:
[1]