JavaScript原型链以及Object,Function之间的关系

JavaScript里任何东西都是对象,任何一个对象内部都有另外一个对象叫__proto__,即原型,它能够包含任何东西让对象继承。固然__proto__自己也是一个对象,它本身也有本身的__proto__,这样一级一级向上,就构成了一个__proto__链,即原型链。固然原型链不会无限向上,它有个终点,能够称为原型链的顶端,或者root,它是一个特殊的对象,它的__proto__为null。javascript

obj.__proto__.__proto__......__proto__ === null;

可是对象并非凭空产生的,它通常是某一个class,或者确切说是构造函数的实例。JavaScript和其它面向对象的语言不太同样,它没有所谓的class,而是用函数来模拟class。定义了一个函数,实际上就定义了一个class,函数自己就是class的constructor,例如:java

function foo() {}
var a = new foo();

这里建立了一个对象a,是foo的一个实例。既然a是一个对象,它就有原型__proto__,那a的__proto__是怎么设定的?这里就引出了prototype,它是函数才有的一个属性,也是一个对象,一个函数建立的实例的__proto__就指向这个函数的prototype。因此a的__proto__被设定为foo.prototype,即:函数

a.__proto__ === foo.prototype;

固然foo.prototype也是个对象,它天然也有__proto__,默认指向Object.prototype,即:prototype

foo.prototype.__proto__ === Object.prototype;

而Object.prototype已经没有原型了,它的__proto__是null。这是JavaScript里一个很特殊的对象,它就是原型链的顶端code

以上就构成了由对象a开始的原型链:对象

a.__proto__ === foo.prototype;
a.__proto__.__proto__ === Object.prototype;

JavaScript所以而推断出:继承

a instanceof foo === true;
a instanceof Object === true;

注意,这就是JavaScript判断一个对象是否instanceof某个函数的依据,即对象a的原型链上有没有一个__proto__是这个函数的prototype,若是有,那么a就是这个函数的instance。因为通常全部的原型链最终都会指向顶端的Object.prototype,因此它们都是Object的instance。ip

prototype和__proto__有什么做用

咱们能够设定原型链

foo.prototype = {
  x : 2,
  y : 3,
}

这里用字面量建立一个对象赋给了foo.prototype,这样foo所建立出来的任何对象的__proto__就都指向了它,所以它们就能够访问里面的field,或者说能够认为它们继承了这些field,例如:原型

var a = new foo();
a.x === 2;
a.y === 3;

不仅是foo.prototype,继续往上foo.prototype的__proto__,以及原型链上全部的__proto__都会被这个对象继承。通常的对象的不少经常使用方法如toString,都是从原型链顶端的Object.prototype里继承来的。

函数也是对象

上面说了,JavaScript里任何东西都是对象,包括函数,能够称为函数对象。因此foo也是对象,那foo的原型__proto__是什么?它是谁的instance?

JavaScript里定义了一个特殊的函数叫Function,能够称做是全部函数的爸爸,全部的函数都是它的实例,所以你能够认为,定义foo的时候发生了这样的事情:

var foo = new Function(args, function_body);

因而咱们有:

foo.__proto__ === Function.prototype;
foo instanceof Function === true;

注意这里的Function.prototype,这也是JavaScript里一个特殊的对象,Chrome的console里要是输入Function.prototype,根本什么也打印不出来,什么native code,就是说它是内部实现的。

这个原型链还没到顶,Function.prototype仍然有原型__proto__,指向Object.prototype,因此咱们最终有:

foo.__proto__.__proto__ === Object.prototype;
foo instanceof Object === true;

如今有个问题来了,那Function本身呢?它其实也是个函数,也是个对象,它的__proto__指向谁?答案是它本身的prototype,即:

Function.__proto__ === Function.prototype;
Function instanceof Function === true;
Function.__proto__.__proto__ === Object.prototype;
Function instanceof Object === true;

总结一下:全部的函数都是Function的instance,Function本身也是它本身的instance,不事后者严格来讲并不许确,Function并非它本身创造本身的,而应该看做JavaScript里原生的一个函数对象,只不过它的__proto__指向了它本身的prototype而已。

Function和Object的关系

这是JavaScript比较奇葩的一个地方,也是不太让人容易接受的一点。

咱们知道通常任何对象都是Object的instance,由于原型链的顶端都指向Object.prototype。那么Object自己是什么?Object也是个函数,而任何函数都是Function的实例对象,好比Array,String,固然Object也包括在内,它也是Function的实例,即:

Object.__proto__ === Function.prototype;
Object instanceof Function === true

同时,Function是个对象,它的原型是Function.__proto__,指向Function.prototype,而且这个原型链向上继续指向Object.prototype,即:

Function.__proto__.__proto__ === Object.prototype;
Function instanceof Object === true

这样就有了一个JavaScript里常常说到的蛋鸡问题:

Object instanceof Function === true
Function instanceof Object === true

到底谁先谁后,谁主谁次?关于这一点网上已经有不少解释,这里首先陈述个人观点,是先有Function,它是主;后有Object,是次。如下是我我的的理解,可能并不许确。要看待这个问题,咱们能够从JavaScript创造世界开始想象:

  1. 咱们知道Object.prototype是原型链的root。但首先,如今世界上尚未Object,更没有Object.prototype。如今只有个特殊的对象,姑且称它为root_prototype,里面定义了些基本的field和method好比toString之类的,之后咱们要让全部的原型链都最终指向它。注意它没有原型,它的__proto__是null,这也是它和全部其它JavaScript对象的区别,使它不同凡响,能有资格成为原型链的root。
  2. 而后定义Function。先看Function的prototype,咱们只要知道这是一个特殊的对象,它的原型__proto__指向刚才的root_prototype,就是说Function.prototype.__proto__ === root_prototype,这样它就算连上了原型链的root。
  3. 上面已经讲过了,Function也是个对象,也有__proto__,指向Function本身的prototype,因此说白了Function也是个奇葩,是JavaScript里规定的一个特殊的东西。而Function.prototype的原型__proto__继续指向root_prototype,因此Function也连上了原型链root。
  4. 全部的函数,什么Array之类的,包括Object也是函数,都是继承Function的,就是说,任意函数foo.__proto__ === Function.prototype,因此咱们天然有Object instanceof Function。
  5. 而后再看Object,它原本就是个函数而已,和其它函数没什么区别,都继承了Function。但是如今最关键的一步是,强行设定让Object.prototype = root_prototype,这样Object.prototype就成了原型链的root!注意这里的逻辑,是先有了root_prototype,而后规定Object.prototype等于它,这一步是人为规定的,这就是Object的特殊之处。你要是规定bar.prototype也等于root_prototype,那bar.prototype也成了原型链的的顶端。因此JavaScript里__proto__这个东西实际上是很随意的,放在哪一个函数的prototype里,哪一个函数就成了你爹。
  6. 好了如今Object.prototype === root_prototype了,成了全部对象原型链的root。那么由第3步的结论,Function也是对象,是连上了root_prototype的,而如今root_prototype给Object.prototype了,那Function天然就是instanceof Object。
总结一下:
- 首先没鸡没蛋,先有一个特殊对象root_prototype,它是上帝。
- 接下来应该是先有Function,而且定义它的prototype和__proto__,都连上了root_prototype。
- 而后才有了Object,它是Function的instance,继承了Function。这时候Object仍然只是个普通的函数。
- 而后规定Object.prototype = root_prototype,这时候Object才开始显得特殊,成为了原型链的顶端,不然它和其它函数根本没什么区别。
- 因而全部的东西,包括Function,都成了Object的instance了。

这里要强调Object和其它函数的不一样之处。Object之因此特殊,就是由于Object的prototype被设定为了root_prototype,仅此而已;而其它函数例如foo,它的prototype只是一个普通的对象,这个对象的__proto__默认状况下指向root_prototype。至于为何这样设定,为何Object会特殊化,大概只是由于Object这个名字起得好,而foo,bar没那么特殊。因此说白了Object函数只是一个盛放root_prototype的容器而已,从而使它晋升为一个特殊的函数。

另外值得注意的是,obj instanceof function 并不意味着obj就是这个function建立出来的,只不过是obj的原型链上有function.prototype而已。

因此所谓的 Object instanceof Function 和 Function instanceof Object 的蛋鸡问题,前者应该来讲是天然而然、无可置疑的,能够认为Object函数是Function创造出来的;然后者说白了只是由于强行规定了Object函数的特殊性,而致使的一个推论,而Function并不是是Object建立的。

固然这些概念绕来绕去讨论其实我感受没什么很大意义,无非只是自圆其说而已。你们写代码时也不会太纠结这些问题,重点仍是要把原型链搞清楚。

相关文章
相关标签/搜索