原型机制是理解对象继承和属性查找的关键
核心概念
- 每个对象都有
__proto__
属性,函数也是对象 - 每个函数(除箭头函数)都有一个
prototype
属性 - 实例对象的
__proto__
指向构造函数的prototype
- 即:
实例对象.__proto__ === 构造函数.prototype
- 即:
prototype
是一个对象,有一个默认的constructor
属性,指向其构造函数构造函数.prototype = { constructor: 构造函数, __proto__: XXX }
- 即:
构造函数.prototype.constructor === 构造函数
注意点
实例无法修改原型的字段
实例无法直接修改(新增、删除、修改)原型上的字段(属性和方法),而是修改自身字段。除非显示调用 实例.__proto__
进行修改
function F() {}
F.prototype.tag = 'F'
F.prototype.printTag = function() { console.log('[TAG]', this.tag) }
const f = new F()
console.log(f.tag) // 'F'
f.printTag() // '[TAG] F'
delete f.tag // true
// 无法删除原型上的字段
// f:
// {
// __proto__: { tag: 'F', printTag: function(), constructor: F, __proto: XXX }
// }
f.tag = 'i am f'
f.printTag() // '[TAG] i am f'
// 无法修改原型上的字段,只是在自身上修改字段
// f:
// {
// tag: 'i am f',
// __proto__: { tag: 'F', printTag: function(), constructor: F, __proto: XXX }
// }
// 调用 printTag() 时,找 this.tag,在自身上找到了,就不向上找原型链了
delete f.tag // true
f.printTag() // '[TAG] F'
// 可以删除自身的字段
// f:
// {
// __proto__: { tag: 'F', printTag: function(), constructor: F, __proto: XXX }
// }
new 时指向
实例对象.__proto__
指向 构造函数.prototype
,是在 new
时发生的。之后如果修改了 构造函数.prototype
指向,之前 new
的实例对象的 __proto__
还指向原来的 构造函数.prototype
function F() {}
F.prototype.tag = 'F'
F.prototype.printTag = function() { console.log('[TAG]', this.tag) }
const f1 = new F()
F.prototype = {
constructor: F,
tag: 'other F',
printTag() { console.log('[--TAG--]', this.tag) }
}
const f2 = new F()
console.log(f1.tag) // 'F'
f1.printTag() // '[TAG] F'
console.log(f2.tag) // 'other F'
f2.printTag() // '[--TAG--] other F'
new
逻辑手写:
Function.prototype.new = function(...args) {
// 1. 创建实例对象
const instance = {}
// 2. 绑定原型
instance.__proto__ = this.prototype
// 或:const instance = Object.create(this.prototype)
// 3. 模拟 ES6 的 new.target
instance.newTarget = this
// 构造函数通过 this.newTarget 访问
// 4. 执行构造函数,并改变 this 指向为实例对象
const res = this.apply(instance, args)
// 5. 构造函数返回非原始类型,则返回该对象
if (res !== null && (typeof res === 'object' || typeof res === 'function')) {
return res
}
// 否则返回实例对象
return instance
}
访问 [[Prototype]]
实例对象.__proto__
是非标准字段,所以不建议使用- 应该使用
Object.getPrototypeOf(实例对象)
或Reflect.getPrototypeOf(实例对象)
获取 - 使用
Object.setPrototypeOf(实例对象)
或Reflect.setPrototypeOf(实例对象)
修改
一览图
两个注意点:
Object.prototype.__proto__ === null
- 理解:
Object.prototype
是对象,所以其__proto__
应该指向Object.prototype
,但这样就死循环了,所以显式规定Object.prototype.__proto__ === null
- 理解:
Function.__proto__ === Function.prototype
- 理解:
Function
是函数,所以Function.__proto__ === Function.prototype
- 理解: