正真理解 JavaScript 中的原型模式是掌握 JavaScript 基础的关键。原型模式是 JavaScript 实现面向对象和继承的基础。而原型中的 __proto__ 与 prototype 又是理解原型模式的关键。
本文用代码结合图片的方式梳理一下 __proto__ 和 prototype 之间的区别和关系。
MDN 中写到:
当谈到继承时,JavaScript 只有一种结构:对象。每个对象都有一个内部链接到另一个对象,称为它的原型 prototype。该原型对象有自己的原型,等等,直到达到一个以 null 为原型的对象。根据定义,null 没有原型,并且作为这个原型链 prototype chain 中的最终链接。
《JavaScript 高级程序设计》第 6 章 6.2.3 原型模式中写到:
当调用构造函数创建一个新实例后,该实例的内部将包含一个指针(内部属性),指向构造函数的原型对象。ECMA-262 第 5 版中称这个指针叫 [[Prototype]]。
大多数浏览器得 ES5 实现中都支持通过属性 __proto__ 来访问 [[Prototype]] 指针。
先看一段代码:
Class 可看作为构造函数的语法糖。
根据这段代码我们可以绘制出如下关系图:
结论
从图中可以看到实例、函数和原型对象之间的关系,得到以下结论:
- 实例、构造函数和原型对象都有 __proto__ 属性,(JavaScript 中所有对象都有此属性,JavaScript 中一切皆对象),指向该对象的构造函数的原型对象;
- 构造函数不仅有 __proto__ 属性,还有 prototype 属性(函数独有属性),prototype 指向该构造函数的原型对象(Note:通过 Function.prototype.bind 方法构造出来的函数是个例外,它没有 prototype 属性);
- 原型链最终指向一个以 null 为原型的对象。null 没有原型,并且作为这个原型链 prototype chain 中的最终链接;
- Function 的 prototype 和 __proto__ 都指向 Function.prototype;