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

时间:2021-09-07 16:59:21

(欢迎一起探讨,如果有什么地方写的不准确或是不正确也欢迎大家指出来~)

PS:

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

一、属性和方法

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

对象属性/对象方法:构造函数内定义的属性/方法

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

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

实例:

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

二、prototype、constructor、__proto__

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

var circle = new Shape();

function test(){
console.log('test')
} console.log(Shape.prototype) // {area: "unknow", move: ƒ, constructor: ƒ}
console.log(Shape.constructor) // ƒ Function() { [native code] }
console.log(Shape.__proto__) // ƒ () { [native code] }
console.log(Shape.prototype.constructor) // ƒ Shape() { ... }
console.log(Shape.prototype.constructor === Shape) // true 构造函数的显式原型的构造器指向构造函数 // Shape的constructor(构造器)指向Function,__proto__(隐式原型)指向Function的原型ƒ () { [native code] }。
// 检测
console.log(Shape.__proto__ === Function.prototype) // true console.log(rect.prototype) // undefined
console.log(rect.constructor) // ƒ Shape() { ... }
console.log(rect.__proto__) // {area: "unknow", move: ƒ, constructor: ƒ} console.log(circle.prototype) // undefined
console.log(circle.constructor) // ƒ Shape() { ... }
console.log(circle.__proto__) // {area: "unknow", move: ƒ, constructor: ƒ} // 综上可以知道,
// 实例的原型均为undefined
// 实例(rect、circle)的隐式原型__proto__指向构造函数(Shape)的显式原型prototype
// 实例(rect、circle)的构造器constructor指向构造其的构造函数(Shape),代表是Shape的实例
// 而构造函数(Shape)是函数Function的实例,所以它的构造器是函数Function
// 那么构造函数(Shape)的隐式原型__proto__指向的是函数Function的显式原型prototype console.log(test.prototype) // {constructor: ƒ} test的原型指向一个包含construct和__proto__的对象
console.log(test.constructor) // ƒ Function() { [native code] }
console.log(test.__proto__) // ƒ () { [native code] } // 那么同理,普通函数的构造器指向Function;普通函数的隐式原型__ptoto__指向Function的显式原型prototype
console.log(test.__proto__ === Function.prototype) // true

所以,

指向 prototype显式原型 constructor构造器 __proto__隐式原型 prototype.constructor(显式原型的构造器)
构造函数 构造函数的原型 Function ƒ () { [native code] } 其本身
实例 undefined 构造其本身的构造函数 构造其本身的构造函数的原型 Cannot read property 'constructor' of undefined
普通函数 一个包含construct和__proto__的对象 Function ƒ () { [native code] } 其本身

综上可以知道

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

  • 实例的隐式原型__proto__,均指向构造其本身的构造函数的显式原型prototype。

  • 构造函数也是函数,它的隐式原型__proto__指向Function的显式原型。

验证一下。

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

可能会有人觉得实例也是函数,所以会有疑问:实例的原型为什么是undefined

来验证一下

// test的原型和rect、circle的原型可能会有疑问
console.log(typeof test) // "function"
console.log(typeof rect + '---' + typeof circle) // "object---object"

每个函数都有prototype属性。

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

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

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

构造函数的原型对象prototype就是通过调用构造函数而创建的那个对象实例的隐式原型。

使用原型对象的好处是可以让所有对象实例共享它所包含的属性和方法。(重点在 共享


PS:

几个方法:

显式原型相关:

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

in 操作符会在通过对象能够访问给定属性时返回true,无论该属性存在于实例还是原型中。

同时使用 hasOwnPropertyin 操作符,就可以确定该属性到底是存在于对象中,还是存在于原型中。

// hasOwnProperty
console.log(rect.hasOwnProperty('color')) // true
console.log(rect.hasOwnProperty('area')) // false // in操作符
console.log('color' in rect); // true
console.log('area' in rect); // true

隐式原型相关:

可以通过 isPrototypeOf 来确定对象之间的关系(__proto__)

可以通过 Object.getPrototypeOf() 得到__proto__的值。

// isPrototypeOf
console.log(Shape.prototype.isPrototypeOf(rect)) // true
console.log(Shape.prototype.isPrototypeOf(test)) // false // Object.getPrototypeOf()
console.log(Object.getPrototypeOf(rect) === Shape.prototype); // true
console.log(Object.getPrototypeOf(test) === Shape.prototype); // false

原型链:

通过原型添加的属性和方法,实例中虽然不包含这些属性和方法,但是却可以使用在构造函数的原型中定义的属性和方法。

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

原型链最终得到的值是null;Object的原型对象是原型链的尽头。


// 隐式原型
console.log(circle.__proto__ === Shape.prototype) // true
console.log(circle.__proto__.__proto__ === Object.prototype) // true
console.log(circle.__proto__.__proto__.__proto__) // null /*
* 以上代码可以理解为
* circle.__proto__ === Shape.prototype
* Shape.prototype.__proto__ === Object.prototype
* Object.prototype.__proto__ === null
*/

额外:

那么Function呢?

console.log(Function.constructor === Function) // true
console.log(Function.constructor === Object) // false // 根据以上输出,可以认为它的__proto__就是它的prototype
console.log(Function.__proto__ === Function.prototype) // true 这句说明Function是Function的实例对象

另外,Function和Object是什么关系呢?

console.log(Object.constructor === Function) // true
console.log(Object.constructor === Object) // false console.log(Object.__proto__ === Function.prototype) // true 这句说明Object是Function的实例对象
// 那么Object也是函数

另外,instanceof的例子

// =====instanceof 用来检测 构造函数的 prototype 属性是否出现在某个实例对象的原型链上
console.log(Object instanceof Function) // true
// 因为Object.__proto__ === Function.prototype console.log(Object instanceof Object) // true
// 因为Object.__proto__ === Function.prototype,Function.prototype.__proto__ === Object.prototype console.log(Function instanceof Function) // true
// 因为 Function.__proto__ === Function.prototype console.log(Function instanceof Object) // true
// 因为 Function.__proto__ === Function.prototype,Function.prototype.__proto__ === Object.prototype

参考:

https://segmentfault.com/q/1010000000249140