Several ways to implement inheritance in JS

from:https://www.cnblogs.com/humin/p/4556820.html

foreword

As an object-oriented weakly typed language, inheritance is also one of its very powerful features. So how to implement inheritance in JS? let us wait and see.

Implementation of JS inheritance

Since we want to implement inheritance, first we have to have a parent class, the code is as follows:

// 定义一个动物类
function Animal (name) {
  // 属性
  this.name = name || 'Animal';
  // 实例方法
  this.sleep = function(){
    console.log(this.name + '正在睡觉!');
  }
}
// 原型方法
Animal.prototype.eat = function(food) {
  console.log(this.name + '正在吃:' + food);
};

1. Prototype chain inheritance

Core:  use the instance of the parent class as the prototype of the child class

function Cat(){ 
}
Cat.prototype = new Animal();
Cat.prototype.name = 'cat';

// Test Code
var cat = new Cat();
console.log(cat.name);
console.log(cat.eat('fish'));
console.log(cat.sleep());
console.log(cat instanceof Animal); //true 
console.log(cat instanceof Cat); //true

Features:

  1. Very pure inheritance relationship, the instance is an instance of the subclass, and it is also an instance of the parent class
  2. The parent class adds prototype methods/prototype properties, which can be accessed by subclasses
  3. Simple and easy to implement

shortcoming:

  1. To add properties and methods to a subclass, it must be new Animal()executed after such a statement, not in the constructor
  2. Multiple inheritance is not possible
  3. The reference property from the prototype object is shared by all instances (see appendix code for details:  Example 1 )
  4. When creating a subclass instance, you cannot pass parameters to the parent class constructor

Recommended index: ★★ (3 and 4 fatal flaws)

2017-8-17 10:21:43 Added: Thanks to  MMHS for  pointing this out. Disadvantage 1 is wrongly described: you can add instance attributes to Cat instances in the Cat constructor. If you want to add prototype properties and methods, they must be new Animal()executed after such a statement.

2. Constructive inheritance

Core: Using the constructor of the parent class to enhance the subclass instance is equivalent to copying the instance attributes of the parent class to the subclass (without using the prototype)

function Cat(name){
  Animal.call(this);
  this.name = name || 'Tom';
}

// Test Code
var cat = new Cat();
console.log(cat.name);
console.log(cat.sleep());
console.log(cat instanceof Animal); // false
console.log(cat instanceof Cat); // true

Features:

  1. Solved the problem of subclass instances sharing parent class reference properties in 1
  2. When creating an instance of a subclass, you can pass parameters to the parent class
  3. Can achieve multiple inheritance (call multiple parent class objects)

shortcoming:

  1. An instance is not an instance of the parent class, just an instance of the child class
  2. Can only inherit instance properties and methods of the parent class, not prototype properties/methods
  3. Function reuse cannot be achieved, each subclass has a copy of the parent class instance function, which affects performance

Recommended index: ★★ (disadvantage 3)

3. Instance inheritance

Core: Add new features to parent class instances and return them as child class instances

function Cat(name){
  var instance = new Animal();
  instance.name = name || 'Tom';
  return instance;
}

// Test Code
var cat = new Cat();
console.log(cat.name);
console.log(cat.sleep());
console.log(cat instanceof Animal); // true
console.log(cat instanceof Cat); // false

Features:

  1. Does not limit the calling method, whether it is new 子类()or is 子类(), the returned object has the same effect

shortcoming:

  1. An instance is an instance of a parent class, not an instance of a child class
  2. Multiple inheritance is not supported

Recommended index: ★★

4. Copy inheritance

function Cat(name){
  var animal = new Animal();
  for(var p in animal){
    Cat.prototype[p] = animal[p];
  }
  Cat.prototype.name = name || 'Tom';
}

// Test Code
var cat = new Cat();
console.log(cat.name);
console.log(cat.sleep());
console.log(cat instanceof Animal); // false
console.log(cat instanceof Cat); // true

Features:

  1. Support for multiple inheritance

shortcoming:

  1. Low efficiency and high memory usage (because the properties of the parent class need to be copied)
  2. Unable to get non-enumerable methods of parent class (non-enumerable methods, cannot be accessed using for in)

Recommended index: ★ (disadvantage 1)

5, composition inheritance

Core: By calling the parent class to construct, inherit the properties of the parent class and retain the advantages of passing parameters, and then use the parent class instance as the child class prototype to achieve function reuse

function Cat(name){
  Animal.call(this);
  this.name = name || 'Tom';
}
Cat.prototype = new Animal();

// 感谢 @学无止境c 的提醒,组合继承也是需要修复构造函数指向的。

Cat.prototype.constructor = Cat;
// Test Code var cat = new Cat(); console.log(cat.name); console.log(cat.sleep()); console.log(cat instanceof Animal); // true console.log(cat instanceof Cat); // true

Features:

  1. Make up for the defect of mode 2, you can inherit instance properties/methods, you can also inherit prototype properties/methods
  2. An instance of both the subclass and the parent class
  3. There is no reference property sharing problem
  4. Passable
  5. Functions can be reused

shortcoming:

  1. The parent class constructor is called twice, and two instances are generated (the subclass instance shields the one on the subclass prototype)

Recommended index: ★★★★ (only consumes a little more memory)

6. Parasitic composition inheritance

Core: Cut off the instance properties of the parent class by parasitic means, so that when the construction of the parent class is called twice, the instance methods/properties will not be initialized twice, avoiding the shortcomings of combined inheritance

function Cat(name){
  Animal.call(this);
  this.name = name || 'Tom';
}
(function(){
  // 创建一个没有实例方法的类
  var Super = function(){};
  Super.prototype = Animal.prototype;
  //将实例作为子类的原型
  Cat.prototype = new Super();
})();

// Test Code
var cat = new Cat();
console.log(cat.name);
console.log(cat.sleep());
console.log(cat instanceof Animal); // true
console.log(cat instanceof Cat); //true

感谢 @bluedrink 提醒,该实现没有修复constructor。

Cat.prototype.constructor = Cat; // 需要修复下构造函数

Features:

  1. perfect

shortcoming:

  1. more complex to implement

Recommended index: ★★★★ (complex implementation, deduct one star)

Addendum code:

Example one:

function Animal (name) {
  // 属性
  this.name = name || 'Animal';
  // 实例方法
  this.sleep = function(){
    console.log(this.name + '正在睡觉!');
  }
  //实例引用属性
  this.features = [];
}
function Cat(name){
}
Cat.prototype = new Animal();

var tom = new Cat('Tom');
var kissy = new Cat('Kissy');

console.log(tom.name); // "Animal"
console.log(kissy.name); // "Animal"
console.log(tom.features); // []
console.log(kissy.features); // []

tom.name = 'Tom-New Name';
tom.features.push('eat');

//针对父类实例值类型成员的更改,不影响
console.log(tom.name); // "Tom-New Name"
console.log(kissy.name); // "Animal"
//针对父类实例引用类型成员的更改,会通过影响其他子类实例
console.log(tom.features); // ['eat']
console.log(kissy.features); // ['eat']

原因分析:

关键点:属性查找过程

执行tom.features.push,首先找tom对象的实例属性(找不到),
那么去原型对象中找,也就是Animal的实例。发现有,那么就直接在这个对象的
features属性中插入值。
在console.log(kissy.features); 的时候。同上,kissy实例上没有,那么去原型上找。
刚好原型上有,就直接返回,但是注意,这个原型对象中features属性值已经变化了。

 

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=324711611&siteId=291194637