深入理解JavaScript中的类继承

由于写本文时全部是在编辑器中边写代码边写感想的,所以,全部思想都写在代码注释里面了

// 类继承

//todo.1 extends 关键字

class Animal {
  constructor(name) {
    this.speed = 0;
    this.name = name;
  }
  run(speed) {
    this.speed = speed;
    console.log(`${this.name} runs with speed ${this.speed}`);
  }
}

// 如果“派生类”使用constructor函数,则必须在constructor调用this之前使用super来调用被继承类的constructor
// 如果“派生类”没有使用constructor函数,则默认会生成一个constructor,代码如下
/**
 * constructor(...args) {
 *     super(...args)
 * }
 */
// 为什么需要super() ?
// 因为“派生类(derived constructor)的构造函数与其他函数之间的区别在于其具有特殊的内部属性[[ConstructorKind]]:derived”
// 这个属性会影响new 的行为; 当通过new执行一个常规函数时,它将创建一个空对象,并将这个空对象赋值给this;
// 但是当继承的constructor执行时,它不会执行此操作,它期望父类的constructor来完成这项工作。因此派生类必须执行super才能执行
// 父类的constructor来完成这项工作,否则this指向的那个对象不会创建,并且程序会报错!
class Rabbit extends Animal {
  constructor(name, color) {
    super(name);
    this.color = color;
  }
}

const rabbit = new Rabbit("兔子", "白色");


//todo.2 深入探究内部原理和[[HomeObject]]
// 让我们先来看一个例子。

const animal = {
    name:'Animal',
    eat() {
        console.log('animal');
    }
}

const tiger = {
    __proto__:animal,
    name:'tiger',
    eat() {
        this.__proto__.eat.call(this);
    }
}

const youngTiger = {
    __proto__:tiger,
    name:'youngTiger',
    eat() {
        this.__proto__.eat.call(this);
    }
}


tiger.eat(); // animal
// youngTiger.eat(); // RangeError: Maximum call stack size exceeded

// 为什么会报错?让我们来深入探究一下

/**
 * 在youngerTiger.eat中
 * this.__proto__.eat.call(this)
 * 等于
 * youngTiger.__proto__.eat.call(this)
 * 等于
 * tiger.eat.call(this)
 * 在tiger.eat中
 * this.__proto__.eat.call(this)
 * 等于
 * youngTiger.__proto__.eat.call(this)
 * 等于
 * tiger.eat.call(this)
 */

// 解决方案:[[HomeObject]]

// 当一个函数被定义为类或者对象方法时,它的 [[HomeObject]] 属性就成为了该对象。
// 然后 super 使用它来解析(resolve)父原型及其方法。

let plant = {
    name:'Plant',
    grow() {
        console.log(`${this.name} growing`);
    }
}

let flower = {
    __proto__:plant,
    grow() {
        super.grow();
    }
}

let greenFlower = {
    __proto__:flower,
    grow() {
        super.grow()
    }
}

greenFlower.grow();//Plant growing

// [[HomeObject]]内置属性是被绑定到JavaScript对象上的,“方法”由哪个对象创建,则
// [[HomeObject]]指向哪个对象,并且[[HomeObject]]一旦创建就是不可变的
// 而且只能被super解析。注意:“方法”不是“函数属性”,
// “方法” {method(){}},“函数属性”{method:function(){}}

// 解析,当对象嵌套继承时,为了避免Maximum call stack size exceeded错误,
// 我们可以使用super来代替 this.__proto__.method.call(this)方法,前提是
// [[HomeObject]]属性存在,并且__proto__存在

推荐阅读:《现代JavaScript教程》- 类继承

相关推荐

发表评论

路人甲

网友评论(0)