一、原型
1. 什么是原型?
每一个js对象(除null外)被创建的时候,就会与之关联另一个对象,这个对象就是我们所说的原型,每一个对象都会从原型中继承属性。
2. prototype和__proto__
- 所有函数都有一个
prototype属性(显式原型),该属性是函数独有的,属性值是一个普通对象。我们可以说这个prototype属性指向该函数的原型对象。- 所有引用类型都有一个
__proto__属性(隐式原型),该属性是对象独有的,因为函数也是对象,所以函数也有__proto__属性,属性值是一个普通对象。__proto__可以帮助我们在原型链上查找某一对象的属性。
所有引用类型的
__proto__属性指向它的构造函数的prototype。
如何来验证引用类型的__proto__属性指向它的构造函数的prototype呢?
举个简单的例子:
function Person() {}
const p = new Person();
以上代码表示创建了一个构造函数Person,并用new关键字实例化该构造函数得到一个对象p。
从控制台输出的结果可以看到,p.__proto__和Person.prototype的值和类型是一样的。
我们试着在构造函数Person的原型对象上增加一个name属性并赋值。
从上图打印结果可以看出,p.__proto__上的确存在一个name属性,且值相同。
所以我们清楚了,实例对象的
__proto__属性指向它的构造函数的prototype属性。
3. constructor 属性
从上图我们可以看出,Person.prototype(或者p.__proto__)上还有constructor属性。
接下来我们简单地介绍下这个属性:
constructor属性也是对象独有的,它是从一个对象指向一个函数,含义就是指向该对象的构造函数,每个对象都有构造函数(本身拥有或继承而来)。
所有对象最终都是由Function构造函数得来,所以constructor属性的终点是Function这个函数,如下图所示:
我们在控制台打印p.constructor,能够得到实例对象p的构造函数Person()。
实例对象p本身不具有constructor属性,而是通过继承得到的。
constructor属性存在于Person.prototype上并指向Person函数,对象p通过__proto__到原型链上查找到并继承了constructor属性,所以对象p才有了constructor属性,并指向它的构造函数Person。
二、原型链
当访问对象的某个属性时,会先从这个对象本身属性上查找,如果没有找到,则会去它的
__proto__隐式原型上查找,即它的构造函数的prototype,如果还没有找到就会再去该构造函数的prototype的__proto__中查找,这样一层一层向上查找就会形成一个链式结构,我们称为原型链。
原型链的终点是
null,即Object.prototype.__proto__===null。
三、对象关系图
四、总结
__proto__和constructor属性是对象所独有的。prototype属性是函数独有的,由于函数也是对象,所以函数也有__proto__和constructor属性。prototype属性的作用就是让该函数所有实例化对象都可以共享公用的属性和方法。__proto__属性的作用就是当访问一个对象的属性时,如果该对象内部不存在这个属性,那么就会去它的__proto__属性所指向的那个对象中去找,直到__proto__属性的终点null,再往上找就相当于在null上取值,会报错。通过__proto__属性将对象连接起来的这条链路就是所谓的原型链。constructor属性的含义就是指向该对象的构造函数,所有函数(此时看成对象)最终的构造函数都指向Function。
参考文档: