老生常谈的 JavaScript 原型链

老生常谈的 JavaScript 原型链

💡 原文中文,约2600字,阅读约需7分钟。
📝

内容提要

本文讲解JavaScript原型链:只有函数有prototype属性,对象通过__proto__连接并沿链查找属性。使用new实例化时会创建对象、执行构造函数并连接原型。ES6之前没有类,继承依靠原型链实现,例如Boxer通过Person.call和设置__proto__继承Person。

🔎

延伸解读

理解 prototype 与 __proto__ 的区别

文章强调只有函数拥有 prototype 属性,而对象没有。对象通过 __proto__ 连接,形成原型链。例如,对象 a 没有 hasOwnProperty 方法,但会沿 __proto__ 找到 Object.prototype 上的方法。直接给对象设置 prototype 只会添加一个普通属性,不会影响原型链。理解这一点是掌握原型链的基础。

new 操作符背后的三步逻辑

使用 new 实例化时,实际发生三件事:创建一个新对象、以该对象为 this 执行构造函数、将新对象的 __proto__ 指向构造函数的 prototype。文章通过手写 createInstance 函数演示了这一过程,帮助读者理解 new 的本质,也解释了为何实例能访问构造函数原型上的方法。

ES6 之前如何实现继承

在 ES6 之前,JavaScript 没有类,继承依靠原型链。文章以 Boxer 继承 Person 为例:在 Boxer 构造函数中调用 Person.call(this, name) 来初始化父类属性,然后设置 Boxer.prototype.__proto__ = Person.prototype 来接通原型链。这样 Boxer 实例既能拥有自身属性,也能访问 Person 原型上的方法。

❓

Q&A

JavaScript 中 prototype 和 __proto__ 有什么区别?

只有函数才有 prototype 属性,对象没有。__proto__ 是一种连接,用于访问所连接对象的属性。对象通过 __proto__ 连接到函数的 prototype,从而形成原型链。

用 new 实例化一个对象时,具体发生了什么?

new 操作主要做三件事:1. 创建一个新对象;2. 以新对象为 this 执行构造函数;3. 将新对象的 __proto__ 连接到构造函数的 prototype,使其能访问原型上的属性。

如何自己实现一个 new 操作符?

可以写一个函数:先创建一个空对象 obj,然后用 klass.call(obj, ...arg) 执行构造函数,接着设置 obj.__proto__ = klass.prototype,最后返回 obj。

ES6 之前 JavaScript 如何实现继承?

ES6 之前没有类,继承依靠原型链。例如,让子类构造函数通过 Parent.call(this, ...) 调用父类构造函数,并设置 Child.prototype.__proto__ = Parent.prototype,从而继承父类的方法。

Object.create 和直接设置 __proto__ 有什么区别?

Object.create 可以直接创建一个对象并指定其原型,而直接设置 __proto__ 需要先创建对象再赋值。两者都能实现继承对象属性,但 Object.create 更简洁。

原型链查找属性时,如果找不到会怎样?

如果沿着 __proto__ 链一直找不到属性,最终会返回 undefined。

🏷️

标签

➡️

继续阅读