彻底理解 js __proto__和prototype

477 阅读3分钟

看过很多篇文章,都没能搞清楚原型链这个玩意儿(回想了下,搞不懂可能是因为脑子里Java类、对象的概念根深蒂固,没有Get到js的设计思路)。今天看到知乎一张图,瞬间清晰,借来分享给大家。

原贴地址贴在这里:js中__proto__和prototype的区别和关系?建议结合评论多看几遍这张图,遍历每一条连线的含义。

image.png

理解要点

JS世界里,万物皆对象,包括函数(Function)

评论里说null不是对象,还有其他primitive types 也不是,说的是没错,但是谷歌浏览器下typeof null是返回object的,只能说规范与实现是有差别的。另外,js作者在最初设计js脚本语言的白皮书里,也多次强调一切皆对象(除null)的设计思想。所以答主这么说,还是说得过去的。

由于(构造)函数也是对象,所以函数的实例化与Java、c++等其他语言类的实例化完全不同。对象的创建/继承是通过原型链完成,即对象可以使用隐式原型__proto__所指向的对象里的方法。个人觉得,js世界里叫原型比较好,参考java、c++那样叫父类、子类是不太合适的。

__proto__(隐式原型)与prototype(显式原型)

  1. 任何对象都有__proto__属性。
  2. 函数也是对象,不仅有__proto__属性,还有prototype属性。
  3. 函数的prototype属性指向的对象叫做prototype对象,即原型对象。原型对象服务于new创建实例的过程,保存了实例对象能访问的公共方法。
  4. new实例对象的过程中,实例对象的__proto__便指向prototype对象,实例对象便可访问原型对象的方法了,这就是js世界中的继承。
  5. 既然函数也是对象,那么函数的__proto__指向谁?
  6. 原型prototype对象的__proto__又指向谁?

构造函数

一定不要按照c++、java里的构造函数来理解js的构造函数,完全不是一回事。c++里构造函数是一个方法,而js里构造函数是一个对象,对象,对象,重要的事情说三遍。js是通过一种特殊的对象生成对象。

认真看上面图片
Foo是构造函数,实例化了f1,那么f1__proto__指向Fooprototype指向的原型对象,f1便可访问原型对象里的方法。

上面的第5个问题,既然Foo也是一个对象,那么Foo.__proto__指向谁?当然是构造Foo对象的构造函数的原型对象,Foo是由Function构造,那么Foo.__proto__ === Function.prototype

上面的第6个问题,Foo原型对象的__proto__又指向谁?原型对象是对象,由构造函数Object()构造,因此Foo.prototype.__proto__ === Object.prototype

结合5、6,我们继续朝前推进,如果我们要研究的对象是o1会有什么不同:
o1Object()构造,因此o1.__proto__ === Object.prototype.
Object()__proto__指向谁?注意,Object()构造函数也是由Function构造,因此Object.__proto__ === Function.prototype
Object.prototypeFunction.prototype等这些原型对象都是由Object()构造,因此Function.prototype.__proto__ === Object.prototype
那终极问题,Object.prototype__proto__指向谁,js规定。Object.prototype.__proto__ === null,所以,原型链的终结都是null

js就是这样一门神器的语言,让人脑壳痛。