理性分析 JavaScript 中的原型

原型

在类继承的语言中,好比 Java ,使用了类来描述实例对象的行为。JavaScript 中没有类,因此也没有使用类继承。采用的是原型继承的方式。javascript

原型继承使用对象来描述实例对象的行为,这个描述行为的对象就是原型对象(prototype)。java

prototype

prototype 是全部函数都具备的属性。当一个函数被做为构造函数生成一个实例对象时,prototype 就是这个实例对象的原型对象。浏览器

constructor

constructor 表示生成该实例对象的构造函数。 可是实例对象是不存在 constructor 属性的,这个属性被保存在了原型对象中。缓存

来看实例:bash

function Foo(){}
var foo1 = new Foo()
console.log(foo1) 
console.log(Foo.prototype) 
console.log(foo1.constructor === Foo.prototype.constructor) 
复制代码

结果以下: 函数

咱们能够看到 foo1 对象中是不包含 constructor 属性的,而 Foo.prototype 中存在 constructor 属性。可是 foo1 的 constructor 值和 Foo.prototype 的 constructor 值倒是相等的。说明 foo1 的 constructor 属性实际上是从 Foo.prototype 继承过来的。这种将属性不保存在自身,却能经过自身访问获得的设计被称为行为委托。post

__proto__

在上图中,咱们看到在 foo1 对象和 Foo.prototype 对象中都有一个属性 __proto__ 。ui

那么 __proto__ 是什么呢? 在继承中,咱们须要一种向上查找的能力去维持继承关系。对于 JavaScript 来讲就是实例对象查找实例原型,子原型对象查找父原型对象,父原型对象继续向上查找直到根原型对象,也就是 Object.prototype,Object.prototype向上查找会获得一个 null 值,指示查找结束。整个查询路径构成了原型链。this

在浏览器的实现中,使用了 __proto__ 属性来缓存原型对象,全部的对象都拥有这个属性。这样对象经过查询 __proto__ 属性便能实现向上查找。spa

来看实例:

function Foo(){}
var foo1 = new Foo()
console.log(foo1.__proto__ === Foo.prototype) // true
console.log(Foo.prototype.__proto__ === Object.prototype) // true
复制代码

实例对象 foo1 的 __proto__ 属性缓存着原型对象 Foo.prototype 。子原型对象 Foo.prototype 的 __proto__ 属性缓存着父原型对象 Object.prototype。

图示

从 constructor、prototype、__proto__ 这三个角度去思考,咱们便能很快的画出整个图示。

先来思考 constructor ,原型对象的构造函数表示生成实例对象的构造函数,由此得出下图:

再来思考 prototype,构造函数的 prototype 就是原型对象。补充得出下图:

最后来思考 __proto__,实例对象的 __proto__属性缓存着原型对象,原型对象的 __proto__缓存着父原型对象。 实例对象是由构造函数使用 new 操做符生成的。补充得出下图:


原型相关

instanceof

instanceof 运算符用来检测一个对象的原型链中是否存在指定构造函数的原型对象。

用法以下:

object instanceof constructor
复制代码

来看实例:

function Foo(){}
var foo1 = new Foo()
console.log(foo1 instanceof Foo) // true
console.log(foo1 instanceof Object) // true 
复制代码

经过图示,咱们能够清楚地看到 Foo.prototype 和 Object.prototype 都位于原型链中。

Object.getPrototypeOf

ECMAScript 5 提供的 Object.getPrototypeOf 能够用来查看实例对象的原型。

function Foo(){}
var foo1 = new Foo()
console.log(Object.getPrototypeOf(foo1 ) === Foo.prototype) // true
console.log(Object.getPrototypeOf(foo1 ) === Object.prototype) //false
复制代码

内建对象和内建函数

关于内建对象:

  • 全部的内建对象都是由 Object() 建立而来。

关于内建函数:

  • 全部的内建函数都是由 Function() 建立而来。

理解上面的两个要点,就能理解下面的例子了

Function.__proto__ === Function.prototype // true 
Object.__proto__ === Function.prototype // true
复制代码

Function 是内建函数是由 Function() 建立而来,因此它的 __proto__ 属性中缓存着 Function.prototype。同理可得Object.__proto__ === Function.prototype

Function.__proto__.__proto__ === Object.prototype // true 
Object.__proto__.__proto__ === Object.prototype // true 
Object.__proto__.__proto__.__proto__ === null // true 
复制代码

Function.__proto__是一个内建原型对象,是由 Object() 建立而来的。因此它的 __proto__属性中缓存着 Object.prototype。同理可得 Object.__proto__.__proto__ === Object.prototype。 既然 Object.__proto__.__proto__ === Object.prototype,而 Object.prototype.__proto__ === null ,因此Object.__proto__.__proto__.__proto__ === null


总结

  • prototype 是全部函数都具备的属性。
  • __proto__ 是全部对象(包括函数)都具备的属性。
  • JavaScript 采用行为委托的方式,来继承 prototype 对象中的属性。实例对象自己并不包含这些属性,是经过原型链查找来得到这些属性的值。
  • 在浏览器实现中,使用了 __proto__ 属性来缓存 prototype 对象,整个 __proto__ 查询路径构成了原型链。
  • constructor 表示生成该实例对象的构造函数。实例对象没有 constructor 属性,这个属性被保存在了原型对象中。

相关知识点

  • 继承
  • class、extends、super、static
  • this
相关文章
相关标签/搜索