prototype

是什么?

每个函数都有一个特殊的属性叫做原型(prototype),这个属性的值是一个对象。即:原型指向一个对象,这个对象包含所有实例共享的属性和方法

作用

可以让所有对象实例共享一些属性和方法

constructor

原型对象里有一个 constructor 属性,它指回该原型对象所在的函数
当有一个对象,但不知道它使用了哪个构造器(例如它来自第三方库),并且需要创建另一个类似的对象时,用这种方法就很方便。但是 constructor 是可以被修改的,所以不是绝对安全。

500

函数的 prototype 可以被修改,但是不影响之前已经通过函数 new 出来的对象的 [[Prototype]],只会影响后面新创建的对象的 [[Prototype]] 指向

__proto__

是什么?

每个对象实例(包括 函数 和 prototype 对象)都有一个隐藏属性 [[Prototype]] (显式名称:__proto____proto__ 是一种访问 [[Prototype]] 的方式,而不是 [[prototype]] 本身,是 [[prototype]] 的访问器属性):指向它的 构造函数 的 原型对象(prototype)

作用

继承原型上的属性和方法,构成原型链,实现基于原型的继承

150

可以通过给一个对象的 __proto__ 赋值来设置这个对象的原型,但是有 2 个限制:

  • 不能形成闭环
  • __proto__ 的值可以是 对象 或 null,其他的类型都会被忽略

__proto__ 是历史遗留的,最好使用 Object.getPrototypeOf()Object.setPrototypeOf() 来操作原型

Warning

更改原型是一个非常缓慢的操作,因为它破坏了对象属性访问操作的内部优化

NOTE

可以通过对象实例访问保存在原型中的值,但却不能通过对象实例重写原型中的值。

因为:当为对象实例添加一个与原型对象中同名的属性时,这个属性就会屏蔽原型对象中的同名属性,而不会修改它。

delete 操作是直接作用于对象的,也不会删除原型上的属性

原型链

  1. 当要查找对象的某个属性时,会先查找对象本身是否存在该属性,如果存在就找到了返回,
  2. 如果不存在,就会在它的 __proto__ 所指的原型对象中寻找,
  3. 同样,原型对象也有 __proto__,这样一层一层向上查找,
  4. 最终查找到 Object.prototype.__proto__ 为 null 的终点时查找完成,就构成了原型链。

1000

instanceof

obj instanceof constructor:检查 obj 的原型链 上是否有 constructor.prototype
即:判断某个构造函数的 prototype 属性是否出现在某个实例对象的 原型链 上

this

this 不受原型链影响,就算调用的是原型上的方法,其方法内部的 this 也按照 this 绑定原则进行设置

没有 __proto__ 的对象

Object.create(null) 可以创建一个空对象,这个对象没有原型([[Prototype]] 是 null)

把这样的对象称为 “very plain” 或 “pure dictionary” 对象,它们甚至比通常的普通对象(plain object){...} 还要简单

构造函数和原型上的属性

function F() {
  this.f1 = function() {
    console.log('构造函数内部')
  }
}
 
F.f2 = function() {
  console.log('构造函数外部')
}
 
F.prototype.f3 = function() {
  console.log('原型上')
}
 
F.f1() // 报错 TypeError: F.f1 is not a function
F.f2() // 构造函数外部
F.f3() // 报错 TypeError: F.f3 is not a function
 
let obj = new F()
obj.f1() // 构造函数内部
obj.f2() // 报错 TypeError: obj.f2 is not a function
obj.f3() // 原型上