JS对象进阶-理解prototype、proto、constructor

JS中最复杂的莫过于prototype、proto和constructor之间的三角关系,搞清楚它们之间的关系对理解JS这门语言很重要,下面是我画的一张关系图,本文以该图为例解释它们之间的关系。

JS对象进阶-理解prototype、proto、constructor

基本概念

function People(){};
var p1 = new People;
var o1 = new Object;
var f1 = new Function;

【构造函数】用来初始化实例对象的函数是构造函数。图中浅绿色的People()、Object()、Function()函数是构造函数。

【实例对象】通过构造函数的new操作符创建的对象是实例对象。图中橘黄色的p1、o1和f1都是实例对象。

【prototype】prototype是构造函数的属性,指向实例的原型对象。同一个构造函数的实例对象拥有相同的原型对象,所以可以使用原型对象实现继承。

function People(){};
var p1 = new People;
var o1 = new Object;
var f1 = new Function;

People.prototype === p1.__proto__; // true
Object.prototype === o1.__proto__; // true
Function.prototype === f1.__proto__; // true

【constructor】constructor是原型对象的属性,指向对应的构造函数。

function People(){};
People.prototype.constructor === People; // true

Object.prototype.constructor === Object; // true
Function.prototype.constructor === Function; // true

由于实例对象可以继承原型对象的属性和方法,所以实例对象也有constructor属性,指向对应的构造函数。

function People(){};
var p1 = new People;
p1.constructor === People; // true

var f1 = new Function;
f1.constructor === Function; // true

var o1 = new Object;
o1.constructor === Object; // true

【proto】 proto是实例对象的属性,指向对应的原型对象。

function People(){};
var p1 = new People;
var f1 = new Function;
var o1 = new Object;

p1.__proto__ === People.prototype; // true
f1.__proto__ === Function.prototype; // true
o1.__proto__ === Object.prototype; // true

详细说明

第一部分

People.prototype不仅是实例对象的原型对象,它自身也是一个实例对象,这也是为什么它有proto属性的原因。既然是实例对象肯定有对应的构造函数和原型对象:它的原型对象是Object.prototype;它的构造函数是People()。

function People(){};

var p1 = new People;
People.prototype.__proto__ === Object.prototype; // true

People.prototype作为原型对象时,自身拥有constructor属性,所以它会覆盖继承自原型对象Object.prototype的constructor属性。

function People(){};

var p1 = new People;
People.prototype.hasOwnProperty('constructor'); // true
People.prototype.constructor === People; // true

Function.prototype和上面类似,当Function.prototype作为实例对象时它的原型对象也是Object.prototype;构造函数是Function()。

Function.prototype.__proto__=== Object.prototype // true
Function.prototype.constructor === Function // true

Object.prototype也和上面类似,不仅是实例对象的原型对象,它自身也是一个实例对象。那么Object.prototype作为实例对象时,它的原型对象和构造函数是什么呢?答案:原型对象是null,构造函数是Object()。或许这也是为什么typeof null === 'object'

// Object.prototype作为实例对象
Object.prototype.__proto__ === null; // true

// Object.prototype作为原型对象
Object.prototype.constructor === Object; // true

第二部分

在JavaScript中,函数是Function类型的实例,所以函数也是对象。如果把函数People、函数Function和函数Object都看做实例,它们的构造函数都是Function(),它们的原型对象都是Function.prototype。

function People(){};
People.__proto__ === Function.prototype;// true
Object.__proto__ === Function.prototype;// true
Function.__proto__ === Function.prototype;// true

实例对象People、Function和Object本身没有constructor属性,它们继承了原型对象Function.prototype的constructor属性。

function People(){};
People.constructor === Function;// true
Object.constructor === Function;// true
Function.constructor === Function;// true

Function.hasOwnProperty('constructor') // false
Object.hasOwnProperty('constructor') // false
People.hasOwnProperty('constructor') // false

所有函数都可以看做是构造函数Function()的new操作的实例化对象,所以它们的原型对象都是Function.prototype。

Function.__proto__ === Function.prototype;// true
Object.__proto__ === Function.prototype;// true
People.__proto__ === Function.prototype;// true

总结

如果要解释原型、原型链和继承,一定要画图

// 原型和原型链
var People = function () {};

var p1 = new People(); // {__proto__: {}}

console.log(p1.__proto__ === People.prototype)

console.log(People.prototype.__proto__ === Object.prototype)

console.log(Object.prototype.__proto__ === null)


// 继承:p1 先去构造函数People上找toString,找不到会沿着原型链去Object上找,这就是继承
console.log(p1.toString); //  ƒ toString() { [native code] }

使用new调用函数的时候,主要做了三个工作:创建实例对象、调用内部的constructor函数初始化this绑定、实例原型绑定

上一篇:java重载方法和类初始化详解


下一篇:2021-09-19 验证:实例对象的constructor属性指向其构造函数