简单易懂的谈谈 javascript 中的继承

知识储备

原型链.png

ECMAScript 2015 中引入的 JavaScript 类实质上是 JavaScript 现有的基于原型的继承的语法糖。类语法不会为JavaScript引入新的面向对象的继承模型。

我们知道 js 并没有类的概念,所谓的类是在原型链的基础之上。例如我们通常所使用的 function 实际上是继承于 Function 类的对象。

function fool(a, b) {
  return a + b
}

var fool = new Function("a, b", "return a+b");

两者所做的事情是相同的,函数 fool 其实是 Function 的一个实例,也就是说 fool.__proto__ === Function.prototype的返回值是 true。

于是问题来了,Function 的构造函数是如何生成的呢?

同时我们知道 Function 并不是这一条原型链的终点,Function 是继承于 Object 的。

那么问题又来了 Object 的构造函数是如何生成的呢?

ECMA中关于 Object 的构造函数是这么描述的:


ECMA.png

是内置对象 %Object%,同样的 Function 的 constructor 也是内置对象 %Function%,事实上这个问题也不重要。我们只需要知道 这两个 constructor 都不是 Function 的实例即可。

原型继承

有对象的概念,就会有子类的概念。在 ES2015 中,新增了 关键字 extends

class ChildClass extends ParentClass { ... }

我们只知道 extends 的作用相当于从 ChildClass 的原型链上可以获取 ParentClass 的原型对象,但是具体做了什么事情呢?
首先我们要明白,如何创建子类。


js权威指南p230.png

核心内容: B的实例继承自B.prototype,后者同样也要继承自A.prototype。

我们知道 B.prototype 和 A.prototype 究其根本 都是 对象(Object),那对象的继承概念是什么?


js权威指南p121.png

于是我们自然而然的写下两行代码,

B.prototype  = new A();
B.prototype.constructor = B;

此时我们开始起草我们的 继承方法 inherit。

  function inherit(SuperClass, ChildClass) {

    ChildClass.prototype = new SuperClass();
    ChildClass.prototype.constructor = ChildClass;

    return ChildClass

  }

我们再看看 babel 里对 extends 的实现:

function _inherits(subClass, superClass) {
  if (typeof superClass !== "function" && superClass !== null) {
    throw new TypeError("Super expression must either be null or a function, not " + typeof superClass);
  }
  subClass.prototype = Object.create(superClass && superClass.prototype, {
    constructor: {
      value: subClass,
      enumerable: false,
      writable: true,
      configurable: true
    }
  });
  if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass)
      :
      subClass.__proto__ = superClass;
}

这里使用了Object.create方法。

image.png

这里其实和以下代码类似,只不过回避了对父类构造函数的调用(父类构造函数内如果有生成实例属性的话,也将在原型链继承了)不至于产生多余的属性。

let obj = new superClass();
obj.constructor = subClass
subClass.prototype = obj

如果要在继承时回避对父类构造函数的调用又不使用 Object.create 方法,可以使用过桥函数,如下代码。

  function inherit2(SuperClass, ChildClass) {
    function F() {
    }

    F.prototype = SuperClass.prototype;

    ChildClass.prototype = new F();
    ChildClass.prototype.constructor = ChildClass;

    return ChildClass;

  }

总结: 其实构造函数的 prototype 就是个普通的对象,只不过他具有特殊意义,同时这个对象 有个 constructor 属性(不是必须的),这个属性指向构造函数本身。

.prototype or .__proto__

babel 里对 extends 的实现时还有一句如下代码:

  if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass)
      :
      subClass.__proto__ = superClass;

说实话我现在不是很能理解其中的意味?毕竟已经使用 Object.create 继承过了,再设置一遍是否多余?

于是我想简单易懂的谈谈 .prototype or .__proto__

  • .prototype
    这个属性似乎是只有函数(构造函数其实和普通函数没有区别,只是我们会用 new 去调用)才有的。

  • .__proto__
    这个属性只要是对象(函数也是对象)都有。

    class Parent {}

    class Child extends Parent {}

    let c1 = new Child()

    let par = {key:1};

    let c2 = Object.create(par)

    /*这里输出应该是 Function 的内容,这里 parent 做为Function 的实例,输出的内容是 Function*/
    console.log(Parent.__proto__) //ƒ () { [native code] }

    /*这里 Child 也类似于做为 Parent 的实例,输出的内容是 Parent*/
    console.log(Child.__proto__)//class Parent {}

    /*这里 c1 做为 Child 的实例,输出的内容是 Child.prototype */
    console.log(c1.__proto__) // Parent {constructor: ƒ}

    /*这里输出了 Object.prototype,因为 par 是 Object的实例*/
    console.log(par.__proto__)  //{constructor: ƒ, __defineGetter__: ƒ, __defineSetter__: ƒ, hasOwnProperty: ƒ, __lookupGetter__: ƒ, …}

    /*这里输出的 是 c2继承的对象 也就是 par */
    console.log(c2.__proto__) //{key: 1}

总结: 函数对象的 __proto__ 指向其 继承的函数/Function.prototype;对象的 __proto__指向其 继承的对象/构造函数的prototype。函数对象的prototype.是一个继承了 其父级函数的 实例(这句话想表达的意思是 Child.prototype.__proto__ === Parent.prototype)。

对象的 __proto__属性是 【非标准的】 但许多浏览器已经实现的属性,现在可以通过标准的Object.getPrototypeOf()Object.setPrototypeOf()访问器来访问对象的原型。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 158,847评论 4 362
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,208评论 1 292
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,587评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,942评论 0 205
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,332评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,587评论 1 218
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,853评论 2 312
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,568评论 0 198
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,273评论 1 242
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,542评论 2 246
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,033评论 1 260
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,373评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,031评论 3 236
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,073评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,830评论 0 195
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,628评论 2 274
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,537评论 2 269

推荐阅读更多精彩内容