我们在理解原型和原型链的时候,喜欢去看概念强行记住,当时以为记住了就懂了可等下次又会忘记,其实是还没有真正弄懂。想要弄懂,就要先知道到底难在哪,理清它们之间的关系,再去看概念性的东西,就轻松很多了。
一、原型和原型链的核心难点拆解
难点 1:__proto__、prototype、constructor 三者的关系(最易混淆)
这三个属性是原型体系的 "三角关系",新手很容易记混、用错,先看核心结论
- prototype:函数独有,指向"原型对象",原型对象里默认有constructor属性。
__proto__:所有对象独有,指向自己的"原型对象"。- constructor:原型对象独有,指向创建该原型对象的构造函数。
核心公式(必记)
js
实例.__proto__ === 构造函数.prototype
构造函数.prototype.constructor === 构造函数
实例.constructor === 构造函数 (本质是通过原型链查找 constructor)
js
function Person(name) {
this.name = name
}
const res = new Person('老王')
// 验证1,实例的隐式原型(__proto__)=== 构造函数的显式原型(prototype)
console.log(res.__proto__ === Person.prototype,) // true
// 验证2,原型对象的constructor指向构造函数
console.log(Person.prototype.constructor === Person) // true
// 验证3,实例的本身没有constructor 是通过原型链找到原型对象的constructor
console.log(res.constructor === Person) //true
原型对象本身也是对象,因此它的 __proto__ 会指向更高层的原型对象,最终形成原型链
js
// Person.prototype 是对象,其 __proto__ 指向 Object.prototype
Person.prototype.__proto__ === Object.prototype; // true
// Object.prototype 是原型链的顶端,其 __proto__ 为 null
Object.prototype.__proto__ === null; // true
注意点:修改 prototype 会影响 constructor
若直接覆盖构造函数的 prototype(而非添加属性),会丢失原有的 constructor(默认指向 Object),需手动恢复
js
// 错误示例:直接覆盖prototype,constructor会指向Object
Person.prototype = {
sayHi: function() { console.log('Hi'); }
};
p.constructor === Object; // true(不符合预期)
// 正确做法:覆盖后手动设置constructor
Person.prototype = {
constructor: Person, // 手动指向原构造函数
sayHi: function() { console.log('Hi'); }
};
p.constructor === Person; // true(恢复预期)
总结三者的核心关系
javascript
构造函数(如Person)
↓ 拥有
prototype → 原型对象(如Person.prototype)
↓ 拥有
constructor → 构造函数(Person)
实例(如p)
↓ 拥有
__proto__ → 原型对象(Person.prototype)
二,原型和原型链的核心理解
简单来说每个函数创建都会默认添加一个prototype的属性,创建每个对象也会有一个__proto__的属性,上面也验证过 实例的__proto__(隐式原型)是指向构造函数的prototype(显示原型的)两者是相等的。
构造函数.prototype也是一个对象,所以也会有一个__proto__,指向的是Object.prototype ,因此Object.prototype 也是一个对象,所以也会有__proto__,Object.prototype.proto 指向的却是null,因为这是原型链的最顶层了,顶层就是null
原型链 :实例会先从自身上找,没找到会通过obj.proto 从原型对象上去找(构造函数.prototype),没找到会通过obj.proto .proto 从原型对象上的原型上去找(构造函数.prototype.proto ),直到最顶层Object.prototype去找没找到,就回去Object.prototype.proto 值为null 结束 返回 undefined
js
function Person(name) {
this.name = name
this.age = 20
}
const obj = new Person('zhangsan')
Person.prototype.age2 = 30
Object.prototype.age3 = 40
console.log(obj.age) //访问的是自身上的属性
console.log(obj.age2) //访问的是原型对象上的属性
console.log(obj.age3) //访问的是原型对象的原型上的属性
/**
* obj={
* console.log(obj.age) 30 先找自身
* __proto__ === 构造函数.prototype {
* console.log(obj.age2) 30 如果自身没有找到 就会去找原型对象
* Person.prototype.__proto__ === Object.prototype ={
* console.log(obj.age3) 40 原型对象上没有 就会去找原型对象上的原型上去找
* }
* Object.prototype.__proto__ {
* 直到最顶层 Object.prototype 它的__proto__ === null 所有原型链的最顶层就是
* }
*
* }
*
* }
* **/