ES6中extends的ES5版本实现

82 阅读2分钟

前言

JavaScript之继承与原型链一文中讲到了继承与原型链相关的基础知识,本文将讲解ES6中的extends是如何实现的。

ES6 extends 做了哪些操作

我们先来看下使用extends常见的写法

class Parent {
    constructor(name) {
        this.name = name;
    }
    static speak(msg) {
        console.log(msg);
    }
    walk() {
        console.log(this.name + ' is walking');
    }
}
class Child extends Parent {
    constructor(name, gender) {
        super(name);
        this.gender = gender;
    }
    sayGender() {
        console.log('my gender is ' + this.gender);
    }
}
let parent = new Parent('Parent');
let child = new Child('Child', 'male');
console.log(parent); // {name: 'Parent'}
Parent.speak('Hi'); // Hi
parent.walk(); // Parent is walking
console.log(child); // {name: 'Child', gender: 'male'}
Child.speak('Welcome'); // Welcome
child.walk(); // Child is walking
child.sayGender(); // my gender is male


我们在父类Parent中定义了构建函数,静态方法speak和方法walk,在子类Child中定义了构建函数,在构造函数中调用了父类的构造函数,并且在子类实例上新增了一个属性gender,另外,子类中新增了一个方法sayGender

通过上面一系列的测试的打印结果来看,子类继承了父类的静态方法,父类实例拥有的属性和方法,子类实例也同样拥有。

我们详细展开一下父类实例parent子类实例child的数据结构。

parent展开如下:

image.png

可以看到,nameparent实例上的属性,而walk则是parent的原型对象上的方法。

child展开如下:

image.png

可以看出,namegenderchild实例上的属性,sayGenderchild的原型对象上的方法,walkchild的原型对象的原型对象(Child.prototype.__proto__)上的方法。

这表明Child.prototype.__proto__等于Parent.prototype,另外Child.__proto__等于Parent,我们可以打印验证一下。

console.log(Child.prototype.__proto__ === Parent.prototype); // true
console.log(Child.__proto__ === Parent); // true

我们可以用下面这张图来表示它们之间的关系,可以看出其中的原型链:

image.png

extendsES5版本实现

通过上面这张关系图,我们就可以写出extends的es5版本,如下所示:

function Parent(name) {
    this.name = name;
}
Parent.speak = function(msg) {
    console.log(msg);
}
Parent.prototype.walk = function() {
    console.log(this.name + ' is walking');
}

function Child(name, gender) {
    // 调用父类构造函数,通过call修改this
    Parent.call(this, name);
    this.gender = gender;
}
Child.prototype.sayGender = function() {
    console.log('my gender is ' + this.gender);
}

// 使用Object.setPrototypeOf
Object.setPrototypeOf(Child.prototype, Parent.prototype);
Object.setPrototypeOf(Child, Parent);

在实际的开发过程中,babel会将ES6 extends的相关代码转换成类似上面这段ES5代码。当然babel的转换代码会更加严谨,包含了很多的polyfill的代码。