浅谈 js 原型和原型链

1,348 阅读3分钟

一、原型

1. 什么是原型?

每一个js对象(除null外)被创建的时候,就会与之关联另一个对象,这个对象就是我们所说的原型,每一个对象都会从原型中继承属性

2. prototype和__proto__

  • 所有函数都有一个prototype属性(显式原型),该属性是函数独有的,属性值是一个普通对象。我们可以说这个prototype属性指向该函数的原型对象
  • 所有引用类型都有一个__proto__属性(隐式原型),该属性是对象独有的,因为函数也是对象,所以函数也有__proto__属性,属性值是一个普通对象。__proto__可以帮助我们在原型链上查找某一对象的属性。

所有引用类型__proto__属性指向它的构造函数prototype

如何来验证引用类型的__proto__属性指向它的构造函数的prototype呢?

举个简单的例子:

function Person() {}
const p = new Person();

以上代码表示创建了一个构造函数Person,并用new关键字实例化该构造函数得到一个对象p

Snipaste_2022-04-26_10-20-05.png

从控制台输出的结果可以看到,p.__proto__Person.prototype的值和类型是一样的。

我们试着在构造函数Person的原型对象上增加一个name属性并赋值。

Snipaste_2022-04-26_10-22-22.png

从上图打印结果可以看出,p.__proto__上的确存在一个name属性,且值相同。

所以我们清楚了,实例对象的__proto__属性指向它的构造函数的prototype属性。

3. constructor 属性

Snipaste_2022-04-26_10-25-11.png

从上图我们可以看出,Person.prototype(或者p.__proto__)上还有constructor属性。 接下来我们简单地介绍下这个属性:

constructor属性也是对象独有的,它是从一个对象指向一个函数,含义就是指向该对象的构造函数每个对象都有构造函数(本身拥有或继承而来)。

所有对象最终都是由Function构造函数得来,所以constructor属性的终点是Function这个函数,如下图所示:

Snipaste_2022-04-24_19-46-23.png

我们在控制台打印p.constructor,能够得到实例对象p的构造函数Person()

Snipaste_2022-04-24_17-44-49.png

实例对象p本身不具有constructor属性,而是通过继承得到的。

constructor属性存在于Person.prototype上并指向Person函数,对象p通过__proto__原型链上查找到并继承了constructor属性,所以对象p才有了constructor属性,并指向它的构造函数Person

二、原型链

当访问对象的某个属性时,会先从这个对象本身属性上查找,如果没有找到,则会去它的__proto__隐式原型上查找,即它的构造函数prototype,如果还没有找到就会再去该构造函数的prototype__proto__中查找,这样一层一层向上查找就会形成一个链式结构,我们称为原型链

原型链的终点是null,即Object.prototype.__proto__===null

三、对象关系图

Snipaste_2022-04-24_19-30-15.png

四、总结

  1. __proto__constructor属性是对象所独有的。 prototype属性是函数独有的,由于函数也是对象,所以函数也有__proto__constructor属性。
  2. prototype属性的作用就是让该函数所有实例化对象都可以共享公用的属性和方法。
  3. __proto__属性的作用就是当访问一个对象的属性时,如果该对象内部不存在这个属性,那么就会去它的__proto__属性所指向的那个对象中去找,直到__proto__属性的终点null,再往上找就相当于在null上取值,会报错。通过__proto__属性将对象连接起来的这条链路就是所谓的原型链
  4. constructor属性的含义就是指向该对象的构造函数,所有函数(此时看成对象)最终的构造函数都指向Function

参考文档:

  1. 帮你彻底搞懂JS中的prototype、__proto__与constructor(图解)
  2. 【原型和原型链】什么是原型和原型链