继承是面向对象编程中相当重要的一个概念,它对帮助代码复用起到了很大的作用。Brendan Eich在创建JavaScript时,没有选择当时最流行的类继承机制,而是借鉴Self,用到了基于原型(prototype)的继承机制,这导致了JavaScript在继承机制方面与Java、C++等基于类继承机制的语言有着显著的区别。其具体在于—C++的多重继承、Java的继承和接口实现中都包含"类"的概念,它们倾向于在创建对象之前[1]已经规定了对象需要继承的类和实现的接口,并且使用类与类之间的继承方式。而在JavaScript中没有"类"的概念,在对象之后所继承的对象也是可以发生动态变化的,并且使用的是对象与对象之间的继承方式。

原型

前言

继承是面向对象编程中相当重要的一个概念,它对帮助代码复用起到了很大的作用。

正文

Brendan Eich在创建JavaScript时,没有选择当时最流行的类继承机制,而是借鉴Self,用到了基于原型(prototype)的继承机制,这导致了JavaScript在继承机制方面与Java、C++等基于类继承机制的语言有着显著的区别。

其具体在于—C++的多重继承、Java的继承和接口实现中都包含"类"的概念,它们倾向于在创建对象之前[1]已经规定了对象需要继承的类和实现的接口,并且使用类与类之间的继承方式。

而在JavaScript中没有"类"的概念,在对象之后所继承的对象也是可以发生动态变化的,并且使用的是对象与对象之间的继承方式。

如果将JavaScript中由string,number,bigint,boolean,null,undefined,symbol组成的基础类型与其对应的值都按下不表,余下的引用类型与其对应的值将都存在着__proto__[2]属性,指向继承的原型对象[3]。

与__proto__存在于每个对象中不同,prototype属性只存在于函数中,在默认的的情况下所有对象的__proto__属性的值与其构造函数下的prototype属性的值是一致的。

let obj = new Object();obj.constructor === Object;//true//obj的__proto__属性的值与obj的构造函数的prototype属性的值指向同一块堆内存obj.__proto__ === obj.constructor.prototype;//trueobj.__proto__ === Object.prototype;//true

所有函数的构造函数都指向Function:

Object.constructor === Function;//true//Object的__proto__属性的值与Object的构造函数的prototype属性的值指向同一块堆内存Object.__proto__ === Object.constructor.prototype;//trueObject.__proto__ === Function.prototype;//trueFunction.constructor === Function;//true//Function的__proto__属性的值与Function的构造函数的prototype属性的值指向同一块堆内存Function.__proto__ === Function.constructor.prototype;//trueFunction.__proto__ === Function.prototype;//true

Function的prototype属性的原型指向Object.prototype:

Object.prototype === Function.prototype.__proto__;//true

不单单是 Function 的prototype属性的原型指向 Object.prototype ,几乎所有的函数[4]的prototype属性的原型指向 Object.prototype ,从V8的测试代码中可见一斑:

最后,位于原型顶点的是Object.prototype.__proto__,它指向 null:

Object.prototype.__proto__ === null;//trueObject.prototype.constructor === Object;//true

根据以上逻辑画的原型指向图:

为了加深理解,我将再定义一个构造函数:

function Person(){}Person.constructor === Function;//truePerson.__proto__ === Person.constructor.prototype;//truePerson.__proto__ === Function.prototype;//true


通过新定义的构造函数创建对象:

let person = new Person();person.constructor === Person;//trueperson.__proto__ === person.constructor.prototype;//trueperson.__proto__ === Person.prototype;//true


根据以上逻辑画的最终的原型指向图:

结尾

了解JavaScript中基于原型(prototype)的继承机制的重点在于捋清楚Object与Function的__proto__、constructor、prototype属性的指向关系,再进一步了解Object和Function各自对应的值以及通过Function对应的值创建出来的对象的__proto__、constructor、prototype属性的指向关系,就大功告成了。
听起来可能有一点点绕,最好是动手画一下图,以后忘记的时候看一下自己画的图就又想起来了。
最后,本人才疏学浅,如有错误之处,还望各位不理赐教。


  1. 这里的措辞最初为程序运行之前,后联想到hotswap,特意改为对象创建后。 ↩︎

  2. 虽然__proto__已经被不推荐使用,但是为了更直观,我在此文中获取对象原型的方法都将通过对象的__proto__属性,还望悉知。 ↩︎

  3. Object.prototype继承的原型指向null。 ↩︎

  4. bind、apply和call没有prototype属性。 ↩︎

©著作权归作者所有:来自51CTO博客作者wx60877cc7b61d6的原创作品,如需转载,请注明出处,否则将追究法律责任

更多相关文章

  1. JavaScript中判断对象是否属于Array类型的4种方法及其背后的原理
  2. JavaScript中的基本字符串与字符串对象的区别
  3. 换肤案例以及选项卡
  4. jq:初学 与 getter/setter方法
  5. h5图片展示和ajax上传
  6. js基础知识:Ajax的get,post异步请求 及 选项卡、换肤实例
  7. jQuery常用dom操作
  8. JavaScript:数学对象,随机生成数字验证码并且随机添加字体颜色
  9. JavaScript:时间对象,实例演示右下角广告图

随机推荐

  1. android hack相关
  2. Linkify匹配电话号码,Email等
  3. android studio更改module名字
  4. Android实现简单短信发送器
  5. AndroidX迁移——弃用support库指南
  6. android 图片设置圆角
  7. Android之fragment的两种创建方式
  8. 【黑马Android】(02)短信发送器/布局演示
  9. Android原生应用的CTS测试步骤
  10. Android RecyclerView添加分隔线 Divider