Шесть реализаций наследования JS

JavaScript

введение

JSВ серии предварительно запланировано 27 статей, от основ до прототипов, до асинхронных, до шаблонов проектирования, до архитектурных шаблонов и т. д.

Эта статьяJS3-я в серии, основная статьяJSНаследование, включая наследование цепочки прототипов, наследование конструктора, наследование композиции, наследование паразитной композиции, наследование прототипов, наследование ES6, множественное наследование и новые .

наследование ES5

Сначала определите родительский класс

function SuperType () {
  // 属性
  this.name = 'SuperType';
}
// 原型方法
SuperType.prototype.sayName = function() {
  return this.name;
};

1. Наследование цепочки прототипов

Основная идея

Использовать экземпляр родительского класса в качестве прототипа дочернего класса

// 父类
function SuperType () {
  this.name = 'SuperType'; // 父类属性
}
SuperType.prototype.sayName = function () { // 父类原型方法
  return this.name;
};

// 子类
function SubType () {
  this.subName = "SubType"; // 子类属性
};

SubType.prototype = new SuperType(); // 重写原型对象,代之以一个新类型的实例
// 这里实例化一个 SuperType 时, 实际上执行了两步
// 1,新创建的对象复制了父类构造函数内的所有属性及方法
// 2,并将原型 __proto__ 指向了父类的原型对象

SubType.prototype.saySubName = function () { // 子类原型方法
  return this.subName;
}

// 子类实例
let instance = new SubType();

// instanceof 通过判断对象的 prototype 链来确定对象是否是某个类的实例
instance instanceof SubType; // true
instance instanceof SuperType; // true

// 注意
SubType instanceof SuperType; // false
SubType.prototype instanceof SuperType ; // true

原型链继承

Функции:

Используя прототипы, разрешите ссылочному типу наследовать свойства и методы другого ссылочного типа.

преимущество:

Наследовать шаблон родительского класса и унаследовать объект-прототип родительского класса

недостаток:

  • Вы можете добавить атрибуты экземпляра к экземплярам подкласса в конструкторе подкласса. Если вы хотитеДобавлены свойства и методы прототипа, он должен быть помещен вSubType.prototype = new SuperType('SubType');такое заявлениеПозжевоплощать в жизнь.

  • Множественное наследование невозможно

  • Все свойства объекта-прототипаДоступно всем экземплярам

    // 父类
    function SuperType () {
      this.colors = ["red", "blue", "green"];
      this.name = "SuperType";
    }
    // 子类
    function SubType () {}
    
    // 原型链继承
    SubType.prototype = new SuperType();
    
    // 实例1
    var instance1 = new SubType();
    instance1.colors.push("blcak");
    instance1.name = "change-super-type-name";
    console.log(instance1.colors); // ["red", "blue", "green", "blcak"]
    console.log(instance1.name); // change-super-type-name
    // 实例2
    var instance2 = new SubType();
    console.log(instance2.colors); // ["red", "blue", "green", "blcak"]
    console.log(instance2.name); // SuperType
    

    prototype-shared

    Уведомление:ИзменятьSuperType свойства ссылочного типа, сделаюSubTypeВсе экземпляры используют это обновление. Обновления свойств базового типа не выполняются.

  • При создании экземпляра подкласса нельзя передавать параметры конструктору родительского класса., или, другими словами, невозможно передать параметры конструктору суперкласса, не затрагивая все экземпляры объекта.

2. Конструктивное наследование

Основная идея:

Вызовите конструктор супертипа внутри конструктора подтипа.

Уведомление:

  • Функция — это не что иное, как объект, который выполняет код в определенном контексте, поэтому здесь используется apply/call.

  • Использование конструктора родительского класса для улучшения экземпляра подкласса эквивалентно копированию атрибутов экземпляра родительского класса в подкласс (без использования прототипа).

// 父类
function SuperType (name) {
  this.name = name; // 父类属性
}
SuperType.prototype.sayName = function () { // 父类原型方法
  return this.name;
};

// 子类
function SubType () {
  // 调用 SuperType 构造函数
  SuperType.call(this, 'SuperType'); // 在子类构造函数中,向父类构造函数传参
  // 为了保证子父类的构造函数不会重写子类的属性,需要在调用父类构造函数后,定义子类的属性
  this.subName = "SubType"; // 子类属性
};
// 子类实例
let instance = new SubType(); // 运行子类构造函数,并在子类构造函数中运行父类构造函数,this绑定到子类

构造函数继承

преимущество:

Решена проблема, заключающаяся в том, что экземпляры подкласса совместно используют ссылочный объект родительского класса в 1, реализуют множественное наследование и могут передавать параметры родительскому классу при создании экземпляра подкласса.

недостаток:

  • Экземпляр не является экземпляром родительского класса, а является экземпляром дочернего класса.
  • ТолькоНаследовать свойства экземпляра и методы родительского класса,не можемНаследовать свойства/методы прототипа
  • Невозможно реализовать мультиплексирование функций, каждый подкласс имеет копию функции экземпляра родительского класса, что влияет на производительность.

3. Комбинированное наследование

Как следует из названия, комбинированное наследование — это шаблон наследования, который сочетает в себе наследование цепочки прототипов и наследование конструктора, чтобы использовать их сильные стороны.

Основная идея:

использоватьСеть прототиповНаследование использует наследование свойств и методов прототипа черезКонструкторНаследование для реализации наследования свойств экземпляра. Таким образом, повторное использование функций может быть достигнуто путем определения методов в прототипе, и можно гарантировать, что каждый экземпляр будет иметь свои собственные свойства.

Вызывая структуру родительского класса, наследуйте свойства родительского класса и сохраняйте преимущества передачи параметров, а затем реализуйте повторное использование функций, используя экземпляр родительского класса в качестве прототипа дочернего класса.

// 父类
function SuperType (name) {
  this.colors = ["red", "blue", "green"];
  this.name = name; // 父类属性
}
SuperType.prototype.sayName = function () { // 父类原型方法
  return this.name;
};

// 子类
function SubType (name, subName) {
  // 调用 SuperType 构造函数
  SuperType.call(this, name); // ----第二次调用 SuperType----
  this.subName = subName;
};

// ----第一次调用 SuperType----
SubType.prototype = new SuperType(); // 重写原型对象,代之以一个新类型的实例

SubType.prototype.constructor = SubType; // 组合继承需要修复构造函数指向
SubType.prototype.saySubName = function () { // 子类原型方法
  return this.subName;
}

// 子类实例
let instance = new SubType('An', 'sisterAn')
instance.colors.push('black')
console.log(instance.colors) // ["red", "blue", "green", "black"]
instance.sayName() // An
instance.saySubName() // sisterAn

let instance1 = new SubType('An1', 'sisterAn1')
console.log(instance1.colors) //  ["red", "blue", "green"]
instance1.sayName() // An1
instance1.saySubName() // sisterAn1

组合继承1

первый звонокSuperTypeПри построении функцииSubType.prototypeполучит два свойстваnameа такжеcolors; при вызовеSubTypeКогда конструктор вызывается второй разSuperTypeКонструктор, на этот раз снова созданный на новом свойстве объектаnameа такжеcolors, эти два свойства будут маскировать одноименные свойства объекта-прототипа.

// instanceof:instance 的原型链是针对 SuperType.prototype 进行检查的
instance instanceof SuperType // true
instance instanceof SubType // true

// isPrototypeOf:instance 的原型链是针对 SuperType 本身进行检查的
SuperType.prototype.isPrototypeOf(instance) // true
SubType.prototype.isPrototypeOf(instance) // true

组合继承2

преимущество:

Он компенсирует дефекты режима 2. Он может наследовать свойства/методы экземпляра и свойства/методы прототипа. Нет проблемы совместного использования эталонных свойств. Параметры можно передавать и использовать повторно.

недостаток:

  • Конструктор родительского класса вызывается дважды, генерируядва экземпляра(Экземпляр подкласса маскирует долю в прототипе подкласса)

4. Наследование паразитарного состава

В составном наследовании конструктор родительского класса вызывается дважды, здесьПаразитными средствами атрибуты экземпляра родительского класса отсекаются, так что при двойном вызове конструкции родительского класса методы/атрибуты экземпляра не будут инициализироваться дважды, избегая недостатков комбинированного наследования.

Главная мысль:

заниматьКонструкторнаследоватьАтрибуты,пройти черезГибридная форма цепи прототиповнаследоватьметод

// 父类
function SuperType (name) {
  this.colors = ["red", "blue", "green"];
  this.name = name; // 父类属性
}
SuperType.prototype.sayName = function () { // 父类原型方法
  return this.name;
};

// 子类
function SubType (name, subName) {
  // 调用 SuperType 构造函数
  SuperType.call(this, name); // ----第二次调用 SuperType,继承实例属性----
  this.subName = subName;
};

// ----第一次调用 SuperType,继承原型属性----
SubType.prototype = Object.create(SuperType.prototype)

SubType.prototype.constructor = SubType; // 注意:增强对象

let instance = new SubType('An', 'sisterAn')

寄生组合

преимущество:

  • позвонить только один разSuperTypeКонструктор, создает только свойство родительского класса
  • Цепочка прототипов остается прежней
  • можно использовать нормальноinstanceofа такжеisPrototypeOf

5. Прототипное наследование

Идеи реализации:

Идея реализации состоит в том, чтобы установить прототип подкласса в прототип родительского класса.

// 父类
function SuperType (name) {
  this.colors = ["red", "blue", "green"];
  this.name = name; // 父类属性
}
SuperType.prototype.sayName = function () { // 父类原型方法
  return this.name;
};

/** 第一步 */
// 子类,通过 call 继承父类的实例属性和方法,不能继承原型属性/方法
function SubType (name, subName) {
  SuperType.call(this, name); // 调用 SuperType 的构造函数,并向其传参 
  this.subName = subName;
}

/** 第二步 */
// 解决 call 无法继承父类原型属性/方法的问题
// Object.create 方法接受传入一个作为新创建对象的原型的对象,创建一个拥有指定原型和若干个指定属性的对象
// 通过这种方法指定的任何属性都会覆盖原型对象上的同名属性
SubType.prototype = Object.create(SuperType.prototype, { 
  constructor: { // 注意指定 SubType.prototype.constructor = SubType
    value: SubType,
    enumerable: false,
    writable: true,
    configurable: true
  },
  run : { 
    value: function(){ // override
      SuperType.prototype.run.apply(this, arguments); 
      	// call super
      	// ...
    },
    enumerable: true,
    configurable: true, 
    writable: true
  }
}) 

/** 第三步 */
// 最后:解决 SubType.prototype.constructor === SuperType 的问题
// 这里,在上一步已经指定,这里不需要再操作
// SubType.prototype.constructor = SubType;

var instance = new SubType('An', 'sistenAn')

原型继承1

множественное наследование

Если вы хотитемножественное наследование, быть пригодным для использованиясмешатьПуть

// 父类 SuperType
function SuperType () {}
// 父类 OtherSuperType
function OtherSuperType () {}

// 多继承子类
function AnotherType () {
    SuperType.call(this) // 继承 SuperType 的实例属性和方法
    OtherSuperType.call(this) // 继承 OtherSuperType 的实例属性和方法
}

// 继承一个类
AnotherType.prototype = Object.create(SuperType.prototype);

// 使用 Object.assign 混合其它
Object.assign(AnotherType.prototype, OtherSuperType.prototype);
// Object.assign 会把  OtherSuperType 原型上的函数拷贝到 AnotherType 原型上,使 AnotherType 的所有实例都可用 OtherSuperType 的方法

// 重新指定 constructor
AnotherType.prototype.constructor = AnotherType;

AnotherType.prototype.myMethod = function() {
     // do a thing
};

let instance = new AnotherType()

Самая важная часть:

  • SuperType.callНаследовать методы свойства экземпляра
  • использоватьObject.create()наследовать свойства и методы прототипа
  • ИсправлятьSubType.prototype.constructorуказывает на

Наследование ES6

Во-первых, реализуйте простое наследование ES6:

class People {
    constructor(name) {
        this.name = name
    }
    run() { }
}

// extends 相当于方法的继承
// 替换了上面的3行代码
class Man extends People {
    constructor(name) {
        // super 相当于属性的继承
        // 替换了 People.call(this, name)
        super(name)
        this.gender = '男'
    }
    fight() { }
}

основной код

extendsОсновной код наследования выглядит следующим образом, и его реализация такая же, как и у вышеупомянутого паразитного комбинированного наследования.

function _inherits(subType, superType) {
    // 创建对象,Object.create 创建父类原型的一个副本
    // 增强对象,弥补因重写原型而失去的默认的 constructor 属性
    // 指定对象,将新创建的对象赋值给子类的原型 subType.prototype
    subType.prototype = Object.create(superType && superType.prototype, {
        constructor: { // 重写 constructor
            value: subType,
            enumerable: false,
            writable: true,
            configurable: true
        }
    });
    if (superType) {
        Object.setPrototypeOf 
            ? Object.setPrototypeOf(subType, superType) 
            : subType.__proto__ = superType;
    }
}

Унаследованные сценарии использования

  • Не используйте их только ради пользы, это пустая трата времени.
  • когда вам нужно создатьРяд объектов с похожими свойствами, затем создайте универсальный объект, который содержит все общие функции, а затем наследуйте эти свойства в более специализированном типе объекта.
  • Следует избегать множественного наследования, вызывающего путаницу.

Примечание:Учитывая то, как работает JavaScript, совместное использование функций между различными объектами часто называютдоверить- Специальные объекты делегируют функциональность универсальным типам объектов. Это может быть более уместно, чем называть это наследованием, потому что «унаследованная» функциональность не копируется в «наследующий» объект, а все еще существует в универсальном объекте.

серия статей

Хотите увидеть больше статей в серии,Нажмите, чтобы перейти на домашнюю страницу блога github.

идти последним

1. ❤️ Получайте удовольствие, продолжайте учиться и всегда продолжайте программировать. 👨💻

2. Если у вас есть какие-либо вопросы или уникальные идеи, пожалуйста, прокомментируйте или свяжитесь с бутылкой напрямую (публичный аккаунт может ответить на 123)! 👀👇

3. 👇Приглашаем подписаться: джентльмен с передними бутылками, обновляемый ежедневно! 👇

前端瓶子君