Coding With Fun
Home Docker Django Node.js Articles Python pip guide FAQ Policy

How inheritance is implemented in javascript


May 30, 2021 Article blog


Table of contents


Class inheritance

//声明父类
//声明父类
function SuperClass() {
  this.superValue = true;
}
//为父类添加共有方法
SuperClass.prototype.getSuperValue = function () {
  return this.superValue;
};
​
//声明子类
function SubClass() {
  this.subValue = false;
}
​
//继承父类
SubClass.prototype = new SuperClass();
//为子类添加共有方法
SubClass.prototype.getSubValue = function () {
  return this.subValue;
};
​
var instance = new SubClass();
console.log(instance.getSuperValue()); //true
console.log(instance.getSubValue()); //false

Class inheritance requires assigning instances of the parent class to the child class prototype, and subClass.prototype inherits superClass. T his class inheritance has two drawbacks. First, because the child class instantiates the parent class through the prototype prototype and inherits the parent class, the common properties in the parent class, if they refer to the type, are shared by all instances in the child class, so the instance change of the child class prototype inherited from the parent class constructor of the common properties will directly affect other children, as follows:

function SuperClass() {
    this.courses = ['语文', '数学', '英语']
}
function SubClass() {}
SubClass.prototype = new SuperClass();
​
var instance1 = new SubClass()
var instance2 = new SubClass()
​
console.log(instance2.courses) //['语文', '数学', '英语']
instance1.courses.push('化学')
console.log(instance2.courses) //['语文', '数学', '英语', '化学']

The modification of instance1 directly affects instance2, which is a disaster trap. S econd, because the inheritance of the child class implementation is implemented by instantiation of the parent class by its prototype prototype, it is not possible to pass parameters to the parent class when the parent class is created, and therefore the properties within the parent class constructor cannot be initialized when the parent class is instantiated. H ow to solve this problem? Please keep looking down.

Constructor inheritance

function SuperClass(current) {
  this.courses = ["语文", "数学", "英语"];
  this.current = current;
}
​
//父类声明原型方法
SuperClass.prototype.getCourses= function () {
  console.log(this.courses);
};
​
//声明子类
function SubClass(current) {
  SuperClass.call(this, current);
}
​
var instance1 = new SubClass("语文");
var instance2 = new SubClass("数学");
​
instance1.courses.push('化学')
console.log(instance1.courses); //["语文", "数学", "英语", "化学"]
console.log(instance1.current); //语文
console.log(instance2.courses); //["语文", "数学", "英语"]
console.log(instance2.current); //数学
​
instance1.getCourses() //TypeError: instance1.getCourses is not a function

SuperClass.call (this, current) statement is the essence of constructor inheritance. B ecause call is a method that changes the working environment of a function, in a child class, calling this call to SuperClass is to execute variables in the child class in the parent class, and because the parent class binds the property to this, the child class inherits the common property of the parent class. B ecause this type of inheritance does not involve prototype prototypes, the prototype method of the parent class is not inherited by the child class, and to be inherited by the child class, you can only place showCourse in the parent class constructor, but this violates the principle of code reuse. In order to combine the advantages of the above two inheritances, there is a combination inheritance.

Combined inheritance

//组合继承function SuperClass(current) {
  //引用类型共有属性
  this.courses = ["语文", "数学", "英语"];
  // 值类型共有属性
  this.current = current;
}
​
SuperClass.prototype.getCourses = function () {
  console.log(this.courses);
};
​
SuperClass.prototype.getCurrent = function () {
  console.log(this.current);
};
​
// 声明子类
function SubClass(current, time) {
  //构造函数继承父类属性
  SuperClass.call(this, current);
  this.time = time;
}
//类式继承 子类原型继承父类
SubClass.prototype = new SuperClass();
//子类原型方法
SubClass.prototype.getTime = function () {
  console.log(this.time);
};

Executing a parent class constructor in a child class constructor, instantiating a parent class on a child class's prototype is a combination pattern, and changing the reference type property courses inherited by the parent class in a child class instance does not change the other instances, as tested below

var instance1 = new SubClass("语文", "9:00");
instance1.getTime(); //9:00
instance1.courses.push('化学')
instance1.getCourses(); //["语文", "数学", "英语", "化学"]
instance1.getCurrent(); //语文
console.log(instance1.current)//语文var instance2 = new SubClass("数学", "10:00");
instance2.getTime(); //10:00
instance2.getCourses(); //["语文", "数学", "英语"]
instance2.getCurrent(); //数学
console.log(instance2.current)//数学

But the pattern executes the parent class function once when executing the child class constructor, and the parent class constructor again when implementing the child class prototype inheritance, calling the parent class constructor twice, which is obviously a design flaw, is there a better way? In response to this defect, a "parasitic composite inheritance" has emerged

Parasitic combinatorial inheritance

Before you can introduce this inheritance, you need to understand "prototype inheritance" and "parasitic inheritance"

Basic understanding

Prototype inheritance

function inheritObject(o) {
  function F() {}
  F.prototype = o;
  return new F();
}
​
var course = {
  name: "语文",
  alikeCourse: ["数学", "英语"],
};
​
var newCourse = inheritObject(course);
newCourse.name = "化学";
newCourse.alikeCourse.push("物理");
​
var otherCourse = inheritObject(course);
otherCourse.name = "政治";
otherCourse.alikeCourse.push("历史");
​
console.log(newCourse.name); //化学
console.log(newCourse.alikeCourse); //["数学", "英语", "物理", "历史"]console.log(otherCourse.name); //政治
console.log(otherCourse.alikeCourse); //["数学", "英语", "物理", "历史"]console.log(course.name); //语文
console.log(course.alikeCourse); //["数学", "英语", "物理", "历史"]

InheritObject can be seen as a encapsulation of class inheritance, where an excess class F is equivalent to a subclass in a class inheritance. The problem of common reference types in class inheritance persists, but there is nothing in the overclass F constructor, so the overhead is small.

Parasitic inheritance

Parasitic inheritance continues to be enhanced on the basis of "prototype inheritance".

function inheritObject(o) {
  function F() {}
  F.prototype = o;
  return new F();
}
​
var course = {
  name: "语文",
  alikeCourse: ["数学", "英语"],
};
​
function createCourse(obj) {
  //通过原型继承方式创建新对象
  var o = new inheritObject(obj);
  // 拓展新对象
  o.getName = function () {
    console.log(this.name);
  };
  return o;
}
​
const newCourse = createCourse(course)

This approach continues to grow attributes within an object, like parasitic growth, so it is called parasitic inheritance. P arasitic inheritance is a secondary encapsulation of prototype inheritance and an extension of inherited objects during secondary encapsulation, so that newly created objects not only have properties and methods in the parent class, but also add new properties and methods. On the basis of this thought, combined with combined inheritance, derived from "parasitic combinatorial inheritance"

implement

function inheritObject(o) {
    function F() {}
    F.prototype = o;
    return new F();
  }
function inheritPrototype(subClass, superClass) {
    //复制一份父类的原型副本保存在变量中
    var p = inheritObject(superClass.prototype)
    //修正因为重写子类原型导致子类的constructor属性被修改
    p.constructor = subClass
    //设置子类的原型
    subClass.prototype = p
}

The parent class prototype above holds a copy and assigns a value to the child class prototype for inheritance, and the parent class function is not recalled once, as tested below, similar to the combined inheritance pattern

//testfunction SuperClass(current) {
  //引用类型共有属性
  this.courses = ["语文", "数学", "英语"];
  // 值类型共有属性
  this.current = current;
}
​
SuperClass.prototype.getCourses = function () {
  console.log(this.courses);
};
​
SuperClass.prototype.getCurrent = function () {
  console.log(this.current);
};
​
// 声明子类
function SubClass(current, time) {
  //构造函数继承父类属性
  SuperClass.call(this, current);
  this.time = time;
}
​
//寄生式继承 子类原型继承父类
inheritPrototype(SubClass, SuperClass);
​
//类式继承 子类原型继承父类
// SubClass.prototype = new SuperClass();//子类原型方法
SubClass.prototype.getTime = function () {
  console.log(this.time);
};
​
var instance1 = new SubClass("语文", "9:00");
var instance2 = new SubClass("数学", "10:00");
​
instance1.getTime(); //9:00
instance1.courses.push("化学");
instance1.getCourses(); //["语文", "数学", "英语", "化学"]
instance1.getCurrent(); //语文
console.log(instance1.current); //语文
​
instance2.getTime(); //10:00
instance2.getCourses(); //["语文", "数学", "英语"]
instance2.getCurrent(); //数学
console.log(instance2.current); //数学

The difference is only

//寄生式继承 子类原型继承父类
inheritPrototype(SubClass, SuperClass);
​
//类式继承 子类原型继承父类
// SubClass.prototype = new SuperClass();

In order to achieve multiple instances of multiple subclasses without affecting each other, the parent class constructor is called only once, which is the ultimate pattern of JavaScript inheritance.