深入理解JS原型链prototype、constructor、__proto__之间的关系

首先来一张图理解JS原型链 :


对于上图的复杂关系其实就来源于3行代码 :

1
2
3
function Foo(){}
var f1 = new Foo();
var f2 = new Foo();
对以上3行代码进行概念解释 : 

实例对象 使用new操作实例化构造函数(Foo)创建出的对象被称为实例对象f1f2就是2个实例对象

构造函数被用来创建实例对象的函数称为构造函数,那么Foo就是f1,f2的构造函数

原型对象 & prototype 构造函数(Foo)拥有prototype属性,一个构造函数(Foo)可以构造出多个实例对象(f1,f2......) ,这些实例对象都拥有相同的原型对象(Foo.prototype)

因此利用构造函数(Foo)的prototype属性,我们可以通过向原型对象(Foo.prototype)中添加属性使实例对象(f1,f2......)继承所添加的属性prototype用法:

1
2
3
4
5
6
function Foo(){}
Foo.prototype.name = 'dalao';
f1 = new Foo();
f2 = new Foo();
console.log(f1.name); //dalao
console.log(f2.name); //dalao

constructor : 原型对象(Foo.prototype)拥有constructor属性,指向该原型对象对应的构造函数(Foo)

1
2
3
4
function Foo(){}
f1 = new Foo();
f2 = new Foo();
console.log(Foo.prototype.constructor === Foo)    //true

实例对象本身不具有constructor属性,但是因为继承性原型对象(Foo.prototype)对应的实例对象(f1,f2......)也都会继承constructor属性并且也指向该原型对象对应的构造函数(Foo)

1
2
3
4
5
6
function Foo(){}
f1 = new Foo();
f2 = new Foo();
console.log(f1 === f2);    //false
console.log(f1.constructor === Foo);    //true
console.log(f2.constructor === Foo);    //true
每个实例对象(f1,f2......)都有一个__proto__属性,指向该实例对象对应的原型对象(Foo.prototype)

1
2
3
4
5
function Foo(){}
f1 = new Foo();
f2 = new Foo();
console.log(f1.__proto__ === Foo.prototype);    //true
console.log(f2.__proto__ === Foo.prototype);    //true
那么其实每个原型对象(Foo.prototype)都可以看做是new Object()创建出的实例对象 :

1
2
function Object(){}
Foo.prototype = new Object();
所以Foo.prototype作为原型对象的同时,自己本身也是实例对象,那么实例对象Foo.prototype对应的原型对象就是Object.prototype

1
2
3
4
function Foo(){}
f1 = new Foo();
f2 = new Foo();
console.log(Foo.prototype.__proto__ === Object.prototype);    //true
虽然原型对象Object.prototype具有属性constructor指向构造方法Object,讲道理作为实例对象Foo.prototype会继承该属性也指向Object

但是Foo.prototype本身就是原型对象,拥有constructor属性,所以会覆盖掉继承constructor属性

1
2
3
4
5
6
function Foo(){}
f1 = new Foo();
f2 = new Foo();
console.log(Object.prototype.constructor === Object);    //true
console.log(Foo.prototype.constructor === Foo);    //true
console.log(Foo.prototype.hasOwnProperty('constructor'))    //true
按照这个逻辑,那么原型对象Object.prototype作为实例对象时,它所对应的原型对象会是什么呢?

1
console.log(Object.prototype.__proto__);    //null

答案是null,那么我们可以由以上结果总结出:

所有原型对象作为实例对象时,它们的原型对象都指向Object.prototype,

所有对象都是Object类型的实例,因此都可以继承Object.prototype上的属性和方法,Object.prototype的原型对象只能是nullnull是一个空对象指针 ,

使Object.prototype不能再向上引用,不然Object.prototype的原型对象继续指向Object.prototype就是循环引用了,这也是一切皆对象的一个论点。

一切皆对象,所有函数也都是对象,无论是Foo()这类自定义的普通函数,还是Object(),Array() , String()这类原生函数都可以看成是由new Function()创造出的对象

1
2
3
function Function(){}
Foo = new Function();
Object = new Function();
所以这些函数作为实例对象时,对应的原型对象便是Funtion.prototype,前面也说过实例对象并不具有constructor属性会继承原型对象的constructor属性

1
2
3
4
5
6
7
8
function Foo(){}
f1 = new Foo();
f2 = new Foo();
console.log(Foo.__proto__ === Function.prototype);    //true
console.log(Object.__proto__ === Function.prototype);    //true
console.log(Array.__proto__ === Function.prototype);    //true
console.log(Foo.hasOwnProperty('constructor'));    //false
console.log(Foo.__proto__.hasOwnProperty('constructor'))    //true
所以当一个函数作为实例对象时 , 它的constructor属性指向构造函数Function :

1
2
3
4
5
6
7
function Foo(){}
f1 = new Foo();
f2 = new Foo();
console.log(Foo.__proto__.constructor === Function);    //true
console.log(Foo.constructor === Function);    //true
console.log(Object.constructor === Function);    //true
console.log(Array.constructor === Function);    //true
同时Function自身也是原生函数,那么它作为实例对象时便可看做是实例化自身结果Funciton原型对象很明显就是Function.prototype构造函数Function :

1
2
3
4
5
function Function(){}    //举例
Function = new Function();    //举例
console.log(Function.__proto__ === Function.prototype);    //true
console.log(Function.__proto__.constructor === Function);    //true
console.log(Function.constructor === Function);    //true

当然上面的2条举例写法显然是不符合语法规范的,这么理解即可,下面3条输出的结果是正确的。

那么结论出来了,所有函数都可以看做是new Function()的结果,作为实例对象,它们对应的原型对象都是Function.prototype

而且前面提过每个原型对象都可以看做是new Object()的结果,它的原型对象是Object.prototype,构造函数是Object

所以当原型对象Function.prototype作为实例对象时,它也不例外,它的原型对象Object.prototype,但因自己拥有constructor属性

所以constructor覆盖掉继承的Object.prototype的属性,指向构造函数Function

1
2
console.log(Function.prototype.__proto__ === Object.prototype);    //true
console.log(Function.prototype.constructor === Function)    //true
经过了以上的详细的分析理解,再回过头来看第一张图,是不是有一种豁然开朗的感觉!最后再归纳总结3条:

0 所有原型对象被当做实例对象时,它们所对应的原型对象是Object.prototype

1 所有函数皆对象,它们所对应的原型对象是Function.prototype,构造函数是Function

2 Object.prototype作为实例对象时,它的__proto__属性指向null,也是一切皆对象的论点

发布评论
还没有评论,快来抢沙发吧!
󰀿
󰀿