探究prototype, __proto__, constructor之间的联系

263 阅读3分钟

前端小白的喃喃自语。

有什么不对的地方,请大神多多指教。

前言

提起prototype、__proto__、constructor,总是离不开对象和function。

在MDN中,明确说明了函数也是一种对象。

In JavaScript, functions are first-class objects, because they can have properties and methods just like any other object. What distinguishes them from other objects is that functions can be called. In brief, they are Function objects.

我们知道,函数拥有原型,对象拥有构造函数。

那么,函数作为对象存在时,它也是拥有构造函数的。

因此,函数既是函数,又是对象。既有constructor,又有prototype。

1. 函数的两面性

1.1 函数的构造函数constructor

自定义函数的构造函数属性,指向Function函数

function Hello() {
    this.a = 1
}
console.log(Hello.constructor) //自定义函数的构造函数指向Function函数
console.log(Hello.constructor === Function)
console.log(Hello.prototype) //Hello函数因为没有重写原型,因此指向默认原型

1.2 函数的原型prototype

函数的原型,是一个对象。

在不重写函数原型的情况下,默认的函数原型里的有contructor属性,它指向该函数本身。

function Hello() {
    this.a = 1
    b() {}
}
console.log(Hello.prototype.constructor === Hello) //true

如果采用直接赋值的方式重写函数原型,就导致原型里的contructor属性丢弃。

想要建立起prototype与原函数之间的联系,需要手动赋值。

function Hello() {
}
console.log(Hello.prototype.contructor === Hello) //true

Hello.prototype = {
greeting () {}
}
console.log(Hello.prototype.contructor === Hello) // false

//需要手动赋值contructor属性来建立起prototype与原函数之间的联系
Hello.prototype.constructor = Hello
console.log(Hello.prototype.contructor === Hello) //true

(这段代码不要粘贴,要自己手敲。不知道为什么,粘贴的不行,谁能告诉我这是为什么?)

因此,一般情况下,函数原型的构造函数,指向该函数本身。

2. 对象

2.1 对象的构造函数constructor

一般情况下,对象的构造函数会指向该函数本身。

new出的对象也一样。

var obj = {}
var arr = []
function Hello() {
    this.a = 1
    this.b = 2
}
var hello = new Hello()
console.log(obj.constructor === Object) //true
console.log(arr.constructor === Array) //true
console.log(hello.constructor === Hello) //true

因此,对象的构造函数的原型,就会指向创建此对象的函数的原型。

function Hello() {
}
var hello = new Hello()
console.log(hello.constructor.prototype === Hello.prototype) //true

2.2 对象的__proto__

当打印一个自定义函数new出来的对象时,可以发现__proto__属性的存在。

对象的__proto__属性,指向了该对象的构造函数的原型。

function Hello() {
    this.a = 1
    this.b = function(){}
}
var hello = new Hello()
console.log(hello.__proto__ === hello.constructor.prototype) //true
console.log(hello.__proto__ === Hello.prototype)

3. prototype、__proto__、constructor关联性总结

3.1 prototype和constructor

  1. 函数原型(prototype),在默认情况下,它的constructor属性指向了该函数本身;
  2. new出来的对象,它的构造函数(constructor),默认指向了该函数本身;
  3. new出来的对象,它的构造函数(constructor)的prototype,默认指向该函数的prototype;
  4. 如果重写函数的原型(直接被赋值),就会斩断函数原型与原函数之间的联系;那么以上三种情况都不将成立。想要重新建立联系,就必须手动给函数原型添加constructor属性,属性值指定为原函数。

3.2 __proto__和prototype

常规对象存在__proto__属性,它默认指向函数的prototype,也就是该对象的构造函数(constructor)的prototype。

4. 拓展

Object.create()

Object.create()方法创建一个新对象,使用现有的对象来提供新创建的对象的__proto__。

The Object.created() method creates a new object, using an existing object as the prototype of the newly created object.

因此,我们通过Object.create()创建一个新对象时,就会改变常规对象的__proto__的指向。

var here = {
    a: 1,
    b() {}
}
let copy = {}
console.log(copy.__proto__ === copy.constructor.prototype) //true
copy = Object.create(here)
console.log(copy.__proto__ === copy.constructor.prototype) //false