理解prototype、__proto__和constructor等关系

理解对象和函数的prototype、__proto__和constructor等关系:

结论在本文的最后。

概述

先给出 构造函数 实例对象 原型对象 三者的关系:

//  概述demo:
function Foo() {};
var f1 = new Foo;

js-prototype-1

如图,

[1]构造函数有一个prototype属性,指向实例对象的原型对象。

[2]原型对象有一个constructor属性,指向该原型对象对应的构造函数。

[3]由于实例对象可以继承原型对象的属性,所以实例对象也拥有constructor属性,同样指向原型对象对应的构造函数。

[4]实例对象有一个__proto__属性,指向该实例对象对应的原型对象。

由于constructor属性是可以继承而来。如果是继承的,我们在图中用虚线来表示。后面所有图同理。

构造函数

用来初始化新创建的对象的函数是构造函数。在上面例子中,Foo()函数是构造函数。

实例对象

可以用一个构造函数,构造多个实例对象。在上面例子中,f1是一个Foo的实例对象。再如:

// 补充demo:
function Foo(){};
var f1 = new Foo;
var f2 = new Foo;
console.log(f1 === f2);//false

原型对象及prototype

[1]构造函数有一个prototype属性,指向实例对象的原型对象。 构造函数Foo()的原型对象是Foo.prototype。

通过同一个构造函数实例化的多个对象具有相同的原型对象。经常使用原型对象来实现继承:

function Foo(){};
Foo.prototype.a = 1;
var f1 = new Foo;
var f2 = new Foo;

console.log(Foo.prototype.a);//1
console.log(f1.a);//1
console.log(f2.a);//1

constructor

[2]原型对象有一个constructor属性,指向该原型对象对应的构造函数。

function Foo(){};
console.log(Foo.prototype.constructor === Foo);//true

[3]由于实例对象可以继承原型对象的属性,所以实例对象也拥有constructor属性,同样指向原型对象对应的构造函数。

function Foo(){};
var f1 = new Foo;
console.log(f1.constructor === Foo);//true

proto

[4]实例对象有一个__proto__属性,指向该实例对象对应的原型对象。 实例对象f1通过__proto__属性也指向原型对象Foo.prototype。

function Foo(){};
var f1 = new Foo;
console.log(f1.__proto__ === Foo.prototype);//true

视Foo.prototype为实例对象

Foo.prototype是f1的原型对象,同时它也可以是实例对象。实际上,任何对象都可以看做是通过Object()构造函数的new操作实例化的对象。

所以,Foo.prototype作为实例对象时,它的构造函数是Object(),原型对象是Object.prototype。

js-prototype-2

相应地,构造函数Object()的prototype属性指向原型对象Object.prototype;

Object.prototype的constructor属性指向构造函数Object()

实例对象Foo.prototype本身具有constructor属性,所以它会覆盖继承自原型对象Object.prototype的constructor属性:

function Foo(){};
var f1 = new Foo;
console.log(Foo.prototype.constructor === Foo);//true
console.log(Object.prototype.constructor === Object);//true
console.log(Foo.prototype.hasOwnProperty('constructor'));//true

实例对象Foo.prototype的__proto__属性同样指向原型对象Object.prototype:

function Foo(){};
var f1 = new Foo;
console.log(Foo.prototype.__proto__ === Object.prototype);//true

视Object.prototype为实例对象

如果Object.prototype作为实例对象的话,其原型对象是什么?结果是null。

js-prototype-3

console.log(Object.prototype.__proto__ === null);//true

函数Foo和函数Object也可以被视为实例对象

任何函数都可以看做是通过Function()构造函数的new操作实例化的结果。

如果把函数Foo当成实例对象的话,其构造函数是Function(),其原型对象是Function.prototype;类似地,函数Object的构造函数也是Function(),其原型对象是Function.prototype

function Foo(){};
var f1 = new Foo;
console.log(Foo.__proto__ === Function.prototype);//true
console.log(Object.__proto__ === Function.prototype);//true

原型对象Function.prototype的constructor属性指向构造函数Function();实例对象Object和Foo本身没有constructor属性,需要继承原型对象Function.prototype的constructor属性:

function Foo(){};
var f1 = new Foo;
console.log(Function.prototype.constructor === Function);//true
console.log(Foo.constructor === Function);//true
console.log(Foo.hasOwnProperty('constructor'));//false
console.log(Object.constructor === Function);//true
console.log(Object.hasOwnProperty('constructor'));//false

“构造函数Foo的constructor”和“构造函数Object的constructor”都是继承而来的,所以都在图中用虚线来表示。

js-prototype-4

视Function为实例对象

所有的函数都可以看成是构造函数Function()的new操作而产生的实例对象。 那么,Function可以看成是调用其自身的new操作的实例化的结果

所以,如果Function作为实例对象,其构造函数是Function,其原型对象是Function.prototype

console.log(Function.__proto__ === Function.prototype);//true
console.log(Function.prototype.constructor === Function);//true
console.log(Function.prototype === Function.prototype);//true

还有一个隐藏关系是Function.constructor === Function结果为true。

证明Functionconstructor是从Function.prototype上“继承”过来:

Object.getOwnPropertyNames(Function) // 没有打印出constructor
Object.getOwnPropertyNames(Function.prototype) // 有打印出constructor

视Function.prototype为实例对象

和前面一样,所有的对象都可以看成是Object()构造函数的new操作的实例化结果。

所以,Function.prototype的原型对象是Object.prototype:

console.log(Function.prototype.__proto__ === Object.prototype);//true

刚才介绍过,Object.prototype的原型对象是null:

console.log(Object.prototype.__proto__ === null);//true

总结

对于代码:

function Foo(){};
var f1 = new Foo;

其涉及到的构造函数 实例对象 原型对象 以及它们之间的关系如图所示:

js-prototype-4

【1】函数(Function也是函数)是new Function的结果,所以函数可以视作实例对象,其构造函数是Function(),原型对象是Function.prototype

【2】一般的对象(除了null和由指定的构造函数所产生的对象) 是new Object的结果,所以一般的对象可以视作实例对象,其构造函数是Object(),原型对象是Object.prototype

【3】Object.prototype的原型对象是null

文章目录
  1. 概述
    1. 构造函数
    2. 实例对象
    3. 原型对象及prototype
    4. constructor
    5. proto
  2. 视Foo.prototype为实例对象
  3. 视Object.prototype为实例对象
  4. 函数Foo和函数Object也可以被视为实例对象
  5. 视Function为实例对象
  6. 视Function.prototype为实例对象
  7. 总结