前端面试中关于原型链的考点主要集中在以下几个方面:
核心概念
- prototype - 函数特有的属性,指向该函数的原型对象
__proto__ - 对象特有的属性,指向其构造函数的原型- constructor - 原型对象的属性,指向构造函数本身
理解:
null > 爷(Object) > 父(构造函数) > 子 (对象)
爷,父,子都有 proto,指向它上级的prototype原型
父 有prototype,原型,只要构造函数有prototype
父 的structor是原型的属性,就是自己
原型链关系
// 基本关系
function Person() {}
const p = new Person();
// 关系链
p.__proto__ === Person.prototype
Person.prototype.constructor === Person
Person.prototype.__proto__ === Object.prototype
Object.prototype.__proto__ === null
new 操作符原理
function myNew(constructor, ...args) {
// 1. 创建空对象
const obj = {}
// 2. 设置原型链
obj.__proto__ = constructor.prototype
// 3. 绑定this并执行构造函数
const result = constructor.apply(obj, args)
// 4. 返回结果
return typeof result === 'object' ? result : obj
}
instanceof 原理实现
function myInstanceof(left, right) {
let proto = left.__proto__
const prototype = right.prototype
while (proto) {
if (proto === prototype) return true
proto = proto.__proto__
}
return false
}
从构造函数 → prototype → __proto__→ 原型链逐层讲解
第一步:构造函数 (Constructor)
构造函数是用来创建特定类型对象的函数。通常约定首字母大写。
function Person(name) {
this.name = name;
}
当用 new调用时,它会创建一个新对象,并将函数内部的 this指向这个新对象。
此时,这个 Person函数有一个特殊的自带属性:prototype。
第二步:prototype (原型对象)
prototype是函数独有的属性。它是一个对象,我们称之为“原型对象”。
当你创建 Person函数时,JavaScript 会自动为你创建这个 Person.prototype对象,并给它一个 constructor属性指回构造函数本身。
console.log(Person.prototype); // 输出:{}
console.log(Person.prototype.constructor === Person); // 输出:true
作用:prototype的主要作用是实现基于原型的继承与共享属性。所有由该构造函数创建的对象,都可以访问到这个原型对象上的属性和方法。
第三步:__proto__(隐式原型)
__proto__是对象(包括函数对象,但这里我们主要讨论实例对象)独有的属性。它指向创建该对象的构造函数的 prototype。
当我们用 new创建实例时:
const p1 = new Person('小明');
实例 p1的内部 [[Prototype]]链(可通过 __proto__属性访问,但不建议在生产环境使用,它是历史遗留的getter)就被设置了。
console.log(p1.__proto__ === Person.prototype); // 输出:true
关键等式:
实例的
__proto__ === 其构造函数的prototype
这意味着,当访问 p1.name时,引擎直接在 p1上找到。当访问 p1.toString()时,引擎会:
- 先在
p1自身上找toString,没找到。 - 通过
p1.__proto__去Person.prototype上找,没找到。 - 再通过
Person.prototype.__proto__继续往上找... 这就形成了“链”。
第四步:原型链 (Prototype Chain)
原型链是由 __proto__链接起来的链式结构,它定义了对象属性的查找机制。
让我们补全链条:
// 1. p1 是 Person 的实例
p1.__proto__ === Person.prototype
// 2. Person.prototype 本身也是一个普通对象,它是谁创建的?
// 答案是:Object
Person.prototype.__proto__ === Object.prototype
// 3. Object.prototype 是原型链的顶端吗?不是,它的 __proto__ 是 null
Object.prototype.__proto__ === null
// 4. 那 Object 函数呢?
// Object 本身是一个构造函数,所以它也有 prototype
Object.prototype.constructor === Object
// 5. 那 Person 函数是谁的实例?函数是 Function 的实例
Person.__proto__ === Function.prototype
Function.prototype.__proto__ === Object.prototype
用图表示这个查找链(以 p1.toString为例):
p1
↓ 查找 toString? 没有
p1.__proto__ → Person.prototype
↓ 查找 toString? 没有
Person.prototype.__proto__ → Object.prototype
↓ 查找 toString? 找到了!调用。
Object.prototype.__proto__ → null (查找终点)
总结与比喻
| 概念 | 归属 | 指向 | 比喻 |
|---|---|---|---|
| 构造函数 (Person) | 函数 | (创建对象的蓝图) | 模具 |
| prototype | 函数的属性 | 原型对象 | 模具附带的共享零件库 |
| 实例 (p1) | 对象 | (具体的产品) | 用模具生产出的产品 |
__proto__ | 实例的属性 | 构造函数的 prototype | 产品的说明书,写着“缺零件时去共享库找” |
| 原型链 | 由 __proto__链接 | 一条从实例到顶端的链 | 按照说明书,去A库找,A库的说明书说去B库找...直到总库。 |
最终答案:原型链的尽头是 null。Object.prototype.__proto__ === null。当查找属性到达 null时,意味着未找到,返回 undefined。
所以,回答时可以这样串起来:
“当我们访问一个对象的属性时,如果对象自身没有,JS 引擎就会通过它的
__proto__去它的构造函数的prototype上找,如果还没有,就继续通过prototype.__proto__往上找,直到原型链顶端null为止。这条由__proto__连接起来的查找路径,就是原型链。”