构造函数原型对象实例隐式原型的理解

Posted hiuman

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了构造函数原型对象实例隐式原型的理解相关的知识,希望对你有一定的参考价值。

(欢迎一起探讨,如果有什么地方写的不准确或是不正确也欢迎大家指点,最后留了一个疑问,欢迎各位大神来探讨~)

PS:

  • 内容中的__proto__可能会被markdown语法导致显示为proto。

  • 建议将构造函数中的方法都定义到构造函数的原型中,由该构造函数创建的实例的方法都会指向同一个方法。(在内部声明的话,每创建一个实例都会重新实例化函数,每个实例中的函数的指向是不同的;如果定义在全局中,且有很多方法,这样毫无封装性可言。)

一、属性和方法

构造函数可以定义三种属性和方法:

对象属性/对象方法:在构造函数中定义的属性或方法

静态属性/静态方法:构造函数的静态属性或方法

原型属性/原型方法:构造函数的原型的属性或方法

(以下,构造函数的对象属性和对象方法 简称 对象属性和对象方法,构造函数的静态属性和方法 简称 静态属性和静态方法,构造函数的原型的属性和方法 简称 原型属性和原型方法)

实例:

function Shape() {
  // 对象属性和对象方法
  this.color = ‘red‘;
  this.show = function(){
    console.log(‘show‘)
  }
}

// 静态属性和静态方法
Shape.type = ‘auto‘
Shape.printing = function(){
  console.log(‘Shape‘)
}

// 原型属性和原型方法
Shape.prototype.area = ‘unknow‘
Shape.prototype.move = function() {
  console.info(‘move‘);
};

// 实例
var rect = new Shape();

console.log(rect.color) // red
console.log(rect.show()) // show

console.log(rect.area) // unknow
console.log(rect.move()) // move

console.log(Shape.type) //auto
console.log(Shape.printing()) //Shape

console.log(rect.type)  // undefined
console.log(rect.printing()) //undefined

构造函数的对象属性和对象方法、原型属性和原型方法,对应的实例都可以调用,构造函数本身调用失败,会提示undefined;

构造函数的静态属性和静态方法,对应的实例调用失败,会提示undefined,但是可由构造函数本身调用。

调用属性、方法 对象属性和对象方法 原型属性和原型方法 静态属性和静态方法
构造函数 undefined undefined 可以得到
实例 可以得到 可以得到 undefined

二、prototype、constructor、proto

在以上的基础上,再实例化一个新的对象和普通函数。

var circle = new Shape();

function test(){
  console.log(‘test‘)
}

console.log(Shape.prototype,‘
‘,Shape.constructor,‘
‘,Shape.__proto__)
console.log(Shape.prototype.constructor,‘
‘,Shape.prototype.constructor ===Shape)

console.log(rect.prototype,‘
‘,rect.constructor,‘
‘,rect.__proto__)
console.log(rect.prototype.constructor,‘
‘,rect.prototype.constructor ===rect)

console.log(circle.prototype,‘
‘,circle.constructor,‘
‘,circle.__proto__)
console.log(circle.prototype.constructor,‘
‘,circle.prototype.constructor ===circle)

console.log(test.prototype,‘
‘,test.constructor,‘
‘,test.__proto__)
console.log(test.prototype.constructor,‘
‘,test.prototype.constructor ===test)

Shape的constructor(构造器)指向Function,proto(隐式原型)指向 ? () { [native code] }。

实例的原型均为undefined,constructor(构造器)指向Shape构造函数(代表是Shape的实例),proto(隐式原型)指向Shape的原型对象。

test的原型指向一个包含construct和__proto__的对象,它的constructor(构造器)指向Function,proto(隐式原型)指向 ? () { [native code] }。

指向 prototype constructor proto prototype.constructor(如果有值,与本身严格比较)
构造函数 构造函数的原型 Function ? () { [native code] } 其本身
实例 undefined 构造其本身的构造函数 构造其本身的构造函数的原型 Cannot read property ‘constructor‘ of undefined
普通函数 一个包含construct和__proto__的对象 Function ? () { [native code] } 其本身

综上可以知道

  • ? () { [native code] } 是Function的原型。

  • 实例的constructor(构造器)都指向构造其本身的构造函数。

  • 而__proto__,均指向构造其本身的构造函数的原型。

验证一下。

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

console.log(rect.__proto__ === rect.constructor.prototype) // true
console.log(rect.__proto__ === Shape.prototype) // true

console.log(circle.__proto__ === circle.constructor.prototype) // true
console.log(circle.__proto__ === Shape.prototype) // true

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

每个函数都有prototype属性。

实例都有一个constructor(构造函数)属性,该属性指向构造它的构造函数。

调用构造函数创建一个新实例后,该实例的内部将包含一个指针(内部属性),指向构造函数的原型对象。

即__proto__属性。

默认情况下,所有原型对象都会自动获得一个constructor属性,这个属性包含一个指向prototype属性所在函数的指针。

如:Shape.prototype.constructor === Shape

prototype就是通过调用构造函数而创建的那个对象实例的原型对象。使用原型对象的好处是可以让所有对象实例共享它所包含的属性和方法。

(重点在 共享

PS:

可以通过hasOwnProperty判断一个对象是否包含自定义属性而不是原型链上的属性。

如果是通过原型添加的属性和方法,实例中虽然不包含属性和方法,但是却可以调用构造函数的原型中定义的方法。这是通过查找对象属性(proto)的过程来实现的。

实例通过__proto__属性去该构造函数的原型中去找属性/方法,如果没有,该构造函数的原型通过__proto__属性去找构造它的构造函数的原型中去找,以此类推。这个通过__proto__属性连接起来的链条就是原型链。原型链最终得到的值是null。

// 此处代表原型链
console.log(circle.__proto__,circle.__proto__ === Shape.prototype) // true
console.log(circle.__proto__.__proto__,circle.__proto__.__proto__ === Object.prototype) // true
console.log(circle.__proto__.__proto__.__proto__) // null

/* 
以上代码可以理解为

circle.__proto__ === Shape.prototype
Shape.prototype.__proto__ === Object.prototype
*/
// 已经知道constructor得到的是“构造”关系
// 那么__proto__形成的是什么关系?

// 或许会认为出现以下情况,注意:__proto__查找的是原型
console.log(circle.__proto__,circle.__proto__ === Function.prototype) // false

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

console.log(circle.__proto__.__proto__,circle.__proto__.__proto__ === Function.prototype) // false

下面来验证一下~

function Shape() {
  this.color = ‘red‘;
  this.show = function(){
    console.log(‘show‘);
  }
}

Shape.prototype.area = ‘unknow‘
Shape.prototype.move = function() {
  console.info(‘move‘);
};

function Rect(){
  Shape.call(this);
  this.color = ‘pink‘;
}

Rect.prototype = new Shape();
Rect.prototype.constructor = Rect;
Rect.prototype.name = ‘Rect‘;

var rect = new Rect();
console.log(rect); // Rect {color: "pink", show: ?}
console.log(rect.__proto__) // Shape {color: "red", show: ?, constructor: ?, name: "Rect"}
console.log(rect.__proto__.__proto__) // {area: "unknow", move: ?, constructor: ?}

由以上代码得出,__proto__得到的是继承关系~


额外:

那么Function呢?

console.log(Function.prototype) // ? () { [native code] }

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

console.log(Function.constructor) // ? Function() { [native code] }  会不会是其本身?
console.log(Function.constructor  === Function) // true
// 为什么???

// 根据以上输出,可以认为它的__proto__就是它的prototype
console.log(Function.__proto__ === Function.constructor.prototype) // true
console.log(Function.__proto__ === Function.prototype) // true

// 检测
console.log(Function instanceof Object) // true

// 疑问!?为什么都指向Function呢?
console.log(Function.constructor) // ? Function() { [native code] }
console.log(Object.constructor) // ? Function() { [native code] }

以上是关于构造函数原型对象实例隐式原型的理解的主要内容,如果未能解决你的问题,请参考以下文章

JavaScript_构造函数/原型/实例对象的关系

js高级_显示原型与隐式原型

理解js中的原型链,prototype与__proto__的关系

JS高阶---显式原型和隐式原型

JavaScript原型规则和实例

理解js中__proto__和prototype的区别和关系