如何用ES5语法实现继承

2019-08-21  本文已影响0人  卡拉咖啦

JS 提供了创建类的语法糖 “class”,以及用与创建子类的关键字 “extends”,那么,问题来了,如果我们不用这些语法糖,我们应该如何实现 JS 中的原型继承呢?

原型链是个什么链

这是我们知道的:

值.proto === 值的构造函数.prototype
这句话的意思是,每个值都有一个隐藏的 proto 属性绑定它的构造函数的 prototype 属性
如果是一个数组,那么
[].proto === Array.prototype
从这个式子表达了一个数组是如何继承数组的原型属性的,而,进一步,数组作为对象的那一部分是怎么继承下来的呢?
答案是:
Array.prototype.proto === Object.prototype

绑定 Object.prototype 的是 Array 构造函数的 prototype 属性。

于是,所谓的“原型链”,本质上就是多个 prototype 串起来的链。

prototype 是个什么东西?

prototype 这个东西,当我们声明一个函数的时候,这个函数就自带一个 prototype 属性,而对象是没有默认的 prototype 属性的(这也是为什么我们说,对象没法被继承,如果还是想继承,需要用到 Object.setPrototypeOf 属性)

继承.png

在没有 class 之前,我们是这么给构造函数写原型属性的:

fn.prototype.a = function(){}

当我们这么写的时候,实际上就意味着,prototype 是一个对象,

于是,所谓的“原型链”,更本质上就是串起来的对象。

proto 与 prototype

当然,仅仅把对象串起来,并不能自动实现“继承”,在整个继承中的关键,其实是“proto”,也就是说,通过 __proto__ 把连起来的对象串,自带“继承”的特效~

到了这里,我们不得不严格区分一下 proto 与 prototype

__proto__怎么看都是 prototype 的缩写,而且 值.__proto__ === 构造函数.prototype 不是说明他们完全等同吗?

但他们确实不是一回事,比如:
1.如果我们不主动设置的话,对象并没有 prototype 属性,但是对象天生就有 proto;
2.我们可以把 __proto__ 理解为有继承的作用,但是 prototype 没有;
3.也就是说,__proto__ 更偏向于表达“绑定、继承”这些动作,而 prototype 表示,被绑定被继承的那堆东西。

进而,如何绑定 __proto__ ? JS 不允许我们手动设置 __proto__
对于我们来说,实现一个有继承效果的 __proto__ 就是使用 new, 别忘了,new 的作用之一就是把新生成的对象 proto 属性绑定原型

构造函数怎么办?

说了半天,我们还没有提构造函数,我们可以这么认为,上面说讲的内容就是构建一个对象的内在关系,而构造函数所要做的就是让我们的对象“实体化” —— 它就是用来产生对象的。

当我们 new 一个构造函数的时候,就会产生一个新的空对象,并把这个对象作为 this 把构造函数执行一遍。

class A {
  constructor(){
    this.x = 1
    console.log("aaa")
  }
}

class B extends A {
  constructor() {
    super()
    this.y = 2
    console.log("bbb")
  }
}

let c = new B
//→ aaa
//→ bbb

console.log(c) //→ {x: 1, y: 2}

看上面的代码,我们发现,子类用于创建对象的时候,不仅子构造函数会执行,父类的构造函数也会执行。

到底我们该怎么做?

于是,如果我们想实现“继承”,应该如何做?关键点在于:
1.子构造函数要调用父构造函数;
2.用 new 实现原型链的继承;

// B 继承 A

function A (){
  this.a = "a"
}
A.prototype.fa = function(){console.log("fa")}

function B(){
  A.apply(this, arguments) // 调用 A 构造函数
  this.b = "b"
}

let f = function(){}
f.prototype = A.prototype
B.prototype = new fn() // 通过 new, B.prototype 就变成了一个有 __proto__ 属性指向 A.prototype 的空对象

B.prototype.fb = function(){console.log("fb")} // 设置其它属性 ……

上一篇下一篇

猜你喜欢

热点阅读