阿里云主机折上折
  • 微信号
Current Site:Index > Implementation of inheritance

Implementation of inheritance

Author:Chuan Chen 阅读数:3368人阅读 分类: JavaScript

The Concept of Inheritance

Inheritance is one of the core concepts of object-oriented programming, allowing an object to acquire the properties and methods of another object. In JavaScript, inheritance is primarily implemented through the prototype chain. After ES6, more intuitive inheritance can also be achieved using the class syntactic sugar.

Prototypal Inheritance

Prototypal inheritance is the most basic form of inheritance, achieved by setting the child class's prototype to an instance of the parent class.

function Parent() {
  this.name = 'parent';
  this.colors = ['red', 'blue'];
}

Parent.prototype.sayName = function() {
  console.log(this.name);
};

function Child() {
  this.age = 10;
}

// Key step: Set Child's prototype to an instance of Parent
Child.prototype = new Parent();

const child1 = new Child();
child1.sayName(); // Output: parent
console.log(child1.age); // Output: 10

Disadvantages of this approach:

  1. All child instances share the reference properties of the parent instance.
  2. Cannot pass arguments to the parent constructor.

Constructor Inheritance

Constructor inheritance is achieved by calling the parent constructor within the child constructor.

function Parent(name) {
  this.name = name;
  this.colors = ['red', 'blue'];
}

function Child(name, age) {
  Parent.call(this, name); // Key step: Call the parent constructor
  this.age = age;
}

const child1 = new Child('Tom', 10);
console.log(child1.name); // Output: Tom
console.log(child1.age); // Output: 10

Advantages:

  1. Solves the issue of shared reference properties in prototypal inheritance.
  2. Allows passing arguments to the parent constructor.

Disadvantages:

  1. Cannot inherit methods from the parent prototype.
  2. The parent constructor is called every time an instance is created.

Combination Inheritance

Combination inheritance combines the advantages of prototypal and constructor inheritance.

function Parent(name) {
  this.name = name;
  this.colors = ['red', 'blue'];
}

Parent.prototype.sayName = function() {
  console.log(this.name);
};

function Child(name, age) {
  Parent.call(this, name); // Second call to Parent
  this.age = age;
}

Child.prototype = new Parent(); // First call to Parent
Child.prototype.constructor = Child;

const child1 = new Child('Tom', 10);
child1.sayName(); // Output: Tom
console.log(child1.age); // Output: 10

Advantages:

  1. Can inherit both instance and prototype properties from the parent.
  2. Allows passing arguments.
  3. Reference properties are not shared.

Disadvantages:

  1. The parent constructor is called twice.

Prototypal Pattern Inheritance

Prototypal pattern inheritance creates new objects based on existing objects, similar to the implementation of Object.create().

function createObject(o) {
  function F() {}
  F.prototype = o;
  return new F();
}

const parent = {
  name: 'parent',
  colors: ['red', 'blue'],
  sayName: function() {
    console.log(this.name);
  }
};

const child = createObject(parent);
child.name = 'child';
child.sayName(); // Output: child

In ES5, the same functionality can be achieved directly using Object.create().

Parasitic Inheritance

Parasitic inheritance enhances objects based on prototypal inheritance.

function createAnother(original) {
  const clone = Object.create(original);
  clone.sayHi = function() {
    console.log('hi');
  };
  return clone;
}

const parent = {
  name: 'parent',
  colors: ['red', 'blue']
};

const child = createAnother(parent);
child.sayHi(); // Output: hi

Parasitic Combination Inheritance

Parasitic combination inheritance is currently the most ideal inheritance approach, solving the issue of calling the constructor twice in combination inheritance.

function inheritPrototype(child, parent) {
  const prototype = Object.create(parent.prototype);
  prototype.constructor = child;
  child.prototype = prototype;
}

function Parent(name) {
  this.name = name;
  this.colors = ['red', 'blue'];
}

Parent.prototype.sayName = function() {
  console.log(this.name);
};

function Child(name, age) {
  Parent.call(this, name);
  this.age = age;
}

inheritPrototype(Child, Parent);

const child1 = new Child('Tom', 10);
child1.sayName(); // Output: Tom
console.log(child1.age); // Output: 10

ES6 Class Inheritance

ES6 introduced the class syntactic sugar, making inheritance more intuitive.

class Parent {
  constructor(name) {
    this.name = name;
    this.colors = ['red', 'blue'];
  }
  
  sayName() {
    console.log(this.name);
  }
}

class Child extends Parent {
  constructor(name, age) {
    super(name); // Call the parent constructor
    this.age = age;
  }
  
  sayAge() {
    console.log(this.age);
  }
}

const child1 = new Child('Tom', 10);
child1.sayName(); // Output: Tom
child1.sayAge(); // Output: 10

Implementing Multiple Inheritance

JavaScript does not natively support multiple inheritance, but it can be simulated using the Mixin pattern.

class A {
  methodA() {
    console.log('method A');
  }
}

class B {
  methodB() {
    console.log('method B');
  }
}

class C {
  constructor() {
    Object.assign(this, new A());
    Object.assign(this, new B());
  }
  
  methodC() {
    console.log('method C');
  }
}

const c = new C();
c.methodA(); // Output: method A
c.methodB(); // Output: method B
c.methodC(); // Output: method C

Inheritance and Performance Considerations

Prototype chain lookups can impact performance, and deep prototype chains can increase lookup time. In performance-sensitive scenarios, consider defining frequently used methods directly on the object.

function createOptimizedObject() {
  const obj = {};
  obj.method1 = function() { /* ... */ };
  obj.method2 = function() { /* ... */ };
  return obj;
}

Inheritance and Design Patterns

Inheritance is applied in various design patterns, such as the Template Method pattern and the Decorator pattern.

// Template Method pattern example
class AbstractClass {
  templateMethod() {
    this.operation1();
    this.operation2();
  }
  
  operation1() {
    throw new Error('operation1 must be implemented');
  }
  
  operation2() {
    throw new Error('operation2 must be implemented');
  }
}

class ConcreteClass extends AbstractClass {
  operation1() {
    console.log('Concrete operation 1');
  }
  
  operation2() {
    console.log('Concrete operation 2');
  }
}

const instance = new ConcreteClass();
instance.templateMethod();

本站部分内容来自互联网,一切版权均归源网站或源作者所有。

如果侵犯了你的权益请来信告知我们删除。邮箱:cc@cccx.cn

上一篇:constructor属性

下一篇:对象拷贝与比较

Front End Chuan

Front End Chuan, Chen Chuan's Code Teahouse 🍵, specializing in exorcising all kinds of stubborn bugs 💻. Daily serving baldness-warning-level development insights 🛠️, with a bonus of one-liners that'll make you laugh for ten years 🐟. Occasionally drops pixel-perfect romance brewed in a coffee cup ☕.