原型机制是理解对象继承和属性查找的关键

核心概念

注意点

实例无法修改原型的字段

实例无法直接修改(新增、删除、修改)原型上的字段(属性和方法),而是修改自身字段。除非显示调用 实例.__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 逻辑手写: ^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]]

一览图

![[Pasted image 20240925124641.png]]

两个注意点:

  1. Object.prototype.__proto__ === null
  2. Function.__proto__ === Function.prototype