super做了什么?

517 阅读1分钟

es6为我们提供了一个新鲜的东西“class",其它中一个super关键字,今天我的主要目的就是想要研究一下super它到底做了一些什么,至于怎么用,这是基础~

一个假设

首先,我要假设super是基于原型链来进行的,即this._proto_.xxx,下面来做一个小例子:

let Person = {
  name:"person",
  eatFood(){
      console.log(`${this.name} can eat food`)
  }
}

let Man = {
  __proto__:Person,
  name:'man',
  eatFood(){
    this.__proto__.eatFood.call(this)
  }
}

let Women = {
  __proto__:Man,
  name:"women",
  eatFood(){
    this.__proto__.eatFood.call(this)
  }
}

Women.eatFood()

这样的一段代码可以符合我们的期望并输出“women can eat food”吗,答案是否定的,它会报如下错误:

VM46624:12 Uncaught RangeError: Maximum call stack size exceeded
    at Object.eatFood (<anonymous>:12:10)
    at Object.eatFood (<anonymous>:12:28)
    at Object.eatFood (<anonymous>:12:28)
    at Object.eatFood (<anonymous>:12:28)
    at Object.eatFood (<anonymous>:12:28)
    at Object.eatFood (<anonymous>:12:28)
    at Object.eatFood (<anonymous>:12:28)
    at Object.eatFood (<anonymous>:12:28)
    at Object.eatFood (<anonymous>:12:28)
    at Object.eatFood (<anonymous>:12:28)

我的浏览器器提示我们,这是一个 RangeError ,超出了最大调用堆栈的大小,一般这种错误提示我们第一想到的,应该是发生一个死循环。

现在让我们来分析一下这段代码,首先,我们执行了 ** Women.eatFood() ** 那么我们会进入到以下步骤里:

Women.eatFood()的方法中为我们提供的this指向了Women对象 可以理解为: Women.__Proto__.eatFood.call(this)
而我们的Women.__proto__ === Man
那么这段代码我们可以直接理解为:Man.eatFood.call(this)

现在,我们缕清了Women.eatFood()中执行的其实是Man.eatFood.call(this),那么我们就需要再缕一下接下来执行了些什么

前文说到 this 指向的是 Women对象,那么问题来了,Man.eatFood.call(this) 中的this依然会指向我们的Women对象 即this.__proto__.eatFood.call(this) === Women.__proto__.eatFood.call(this) === Man.eatFood.call(this)
这是一个无限循环的过程,我们的Man.eatFood()在一直不停地调用着自身

[[HomeObject]]

在这个时候,我们已经发现我们之前的假设是行不通的,我们仅仅通过this并不能将我们的对象记住~

幸运的是,javascript为函数添加了一个内部属性[[HomeObject]],[HomeObject]是函数作为方法绑定到的对象,而我们可以通过使用super来解析对象的原型及方法

同样地,我们还是使用上面的这个例子进行简单的改造:

let Person = {
  name:"person",
  eatFood(){
      console.log(`${this.name} can eat food`)
  }
}

let Man = {
  __proto__:Person,
  name:'man',
  eatFood(){
    super.eatFood()
  }
}

let Women = {
  __proto__:Man,
  name:"women",
  eatFood(){
    super.eatFood();
  }
}

Women.eatFood()

而关于[[HomeObject]]的一些描述,我们可以在ECMA-262的文档中进行查找 adfadfaa

[[HomeObject]]是一个永久性的绑定,并且我们并能对它进行更改。

也就是说,如果我们复制一个使用super的函数,那么它的[[HomeObject]]并不会随着你的复制而更改,对于这种情况,我们往往可能会得到不如预期的结果

还有一点需要注意的是,在对象中使用的时候,对于使用函表达式声明的方法是不会具有[[HomeObject]]的,说人话就是这种情况下使用super会报: Uncaught SyntaxError: 'super' keyword unexpected here