[Внешний словарь] Наследование (2) - Восемь способов обратной записи

внешний интерфейс JavaScript

предисловие

ПредыдущийЯ рассмотрел основы наследования — прототипы и цепочки прототипов. Я очень рад видеть, что кто-то что-то получает после прочтения моего технического обмена, я также смиренно принимаю чье-то мнение.

Прежде чем говорить о нескольких способах наследования, я намерен сначала рассказать о нем — «Кун Иджи».

Что меня больше всего впечатлило в статье «Конг Ицзи», так это действие Конг Джии, линия диалога и вопрос.

Одно действие: сбросить девять монет
Строка диалога: Кража книг не может считаться воровством… Можно ли считать воровством дело ученого?
Вопрос: Как написать хуэйский характер Хуэй Сяндоу

Такой ученый, как Кун Ицзи, глубоко отравленный имперским экзаменационным образованием, часто обращал внимание на некоторые бесполезные символы и считал их знаниями и навыками. Способность знать, как писать «хуэй»?

Я думал об этом. Кажется, интервьюер отвечает: я не знаю, есть ли несколько способов написать return, но я хочу знать, сколько способов можно написать наследование.

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

Введите вопрос

существуетJavaScriptсерединанаследоватьявляется очень важным понятием. Нам нужно понять, пожалуйста, дайте больше советов.

Цель: упростить логику и структуру кода и добиться повторного использования кода.

Далее, давайте изучим следующие 8 видовJavaScriptРеализовать метод наследования.

реализация наследства

Рекомендуемое наследование композиции (4), паразитарное наследование композиции (7), наследование ES6 (8)

1. Метод цепочки прототипов (с использованием прототипа)

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

код показывает, как показано ниже

function staff(){ 
  this.company = 'ABC';
}
staff.prototype.companyName = function(){
  return this.company; 
}
function employee(name,profession){
  this.employeeName = name;
  this.profession = profession;
}
// 继承 staff
employee.prototype = new staff();
// 将这个对象的 constructor 手动改成 employee,否则还会是 staff
employee.prototype.constructor = employee;
// 不使用对象字面量方式创建原型方法,会重写原型链
employee.prototype.showInfo = function(){
  return this.employeeName + "'s profession is " + this.profession;
}
let instance = new employee('Andy','front-end');

// 测试 
console.log(instance.companyName()); // ABC
console.log(instance.showInfo());    // "Andy's profession is front-end"
// 通过 hasOwnProperty() 方法来确定自身属性与其原型属性
console.log(instance.hasOwnProperty('employeeName'))     // true
console.log(instance.hasOwnProperty('company'))          // false
// 通过 isPrototypeOf() 方法来确定原型和实例的关系
console.log(employee.prototype.isPrototypeOf(instance)); // true
console.log(staff.prototype.isPrototypeOf(instance));    // true
console.log(Object.prototype.isPrototypeOf(instance));   // true

существующие проблемы

Самые большие проблемы с наследованием реализации цепочки прототипов:

Когда значение ссылочного типа существует в прототипе, экземпляр может изменить его значение.

function staff(){ 
  this.test = [1,2,3,4];
}
function employee(name,profession){
  this.employeeName = name;
  this.profession = profession;
}
employee.prototype = new staff();
let instanceOne = new employee();
let instanceTwo = new employee();
instanceOne.test.push(5);
console.log(instanceTwo.test); // [1, 2, 3, 4, 5]

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

резюме

  1. На основе конструкторов и цепочек прототипов
  2. пройти черезhasOwnProperty()метод определения собственных свойств и свойств их прототипов
  3. пройти черезisPrototypeOf()метод для определения отношения между прототипом и экземпляром
  4. Значение ссылочного типа в прототипе можно изменить в экземпляре

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

Отличие этого метода от первого заключается в том, что:

employee.prototype = new staff();

Измените его на:

Employee.prototype = Person.prototype;

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

  1. Нет необходимости создавать новые экземпляры объектов при построении отношений наследования.
  2. Поскольку объект-прототип является общим, нет необходимости проходить цепочку прототипов при доступе к объекту, и эффективность, естественно, высока.

недостаток

  1. Как и в случае с методом 1, изменения дочернего объекта повлияют на родительский объект.

резюме

  1. Основан на конструкторе, цепочка прототипов не используется.
  2. Дочерний объект и родительский объект совместно используют объект-прототип

В-третьих, позаимствовать метод конструктора

Этот метод может решить проблему изменения значения ссылочного типа в прототипе.

function staff(){ 
  this.test = [1,2,3];
}
staff.prototype.companyName = function(){
  return this.company; 
}
function employee(name,profession){
  staff.call(this);	
  this.employeeName = name;
  this.profession = profession;
}
// 不使用对象字面量方式创建原型方法,会重写原型链
employee.prototype.showInfo = function(){
  return this.employeeName + "'s profession is " + this.profession;
}
let instanceOne = new employee('Andy','front-end');
let instanceTwo = new employee('Mick','after-end');
instanceOne.test.push(4);
// 测试 
console.log(instanceTwo.test);    // [1,2,3]
// console.log(instanceOne.companyName()); // 报错
// 通过 hasOwnProperty() 方法来确定自身属性与其原型属性
console.log(instanceOne.hasOwnProperty('test'))          // true
// 通过 isPrototypeOf() 方法来确定原型和实例的关系
console.log(staff.prototype.isPrototypeOf(instanceOne));    // false

Из вышеприведенных результатов видно, что:

  1. Заимствование метода конструктора может решить проблему изменения значения ссылочного типа в прототипе.
  2. НоinstanceOneа такжеstaffЦепочки прототипов больше нет

недостаток

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

В-четвертых, наследование композиции (рекомендуется)

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

function staff(){ 
  this.company = "ABC";	
  this.test = [1,2,3];
}
staff.prototype.companyName = function(){
  return this.company; 
}
function employee(name,profession){
  // 继承属性
  staff.call(this);	
  this.employeeName = name;
  this.profession = profession;
}
// 继承方法
employee.prototype = new staff();
employee.prototype.constructor = employee;
employee.prototype.showInfo = function(){
  return this.employeeName + "'s profession is " + this.profession;
}

let instanceOne = new employee('Andy','front-end');
let instanceTwo = new employee('Mick','after-end');
instanceOne.test.push(4);
// 测试 
console.log(instanceTwo.test);    // [1,2,3]
console.log(instanceOne.companyName()); // ABC
// 通过 hasOwnProperty() 方法来确定自身属性与其原型属性
console.log(instanceOne.hasOwnProperty('test'))          // true
// 通过 isPrototypeOf() 方法来确定原型和实例的关系
console.log(staff.prototype.isPrototypeOf(instanceOne));    // true

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

  1. Методы, определенные в прототипах, можно использовать повторно.
  2. Это может гарантировать, что каждая функция имеет свои собственные свойства, что может решить проблему изменения значения ссылочного типа в прототипе.

недостаток

  1. staffбудет вызываться 2 раза: 1-йemployee.prototype = new staff();, второй раз позвонитьstaff.call(this).

5. Наследование прототипа — Object.create()

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

function object(obj){
  function F(){}
  F.prototype = obj;
  return new F();
}

По сутиobject()выполняет поверхностную копию переданного объекта, копируя конструкторFПрототип указывает непосредственно на переданный объект.

var employee = {
  test: [1,2,3]
}

let instanceOne = object(employee);
let instanceTwo = object(employee);
// 测试 
instanceOne.test.push(4);
console.log(instanceTwo.test); // [1, 2, 3, 4]

недостаток

  1. Значение ссылочного типа в прототипе будет изменено
  2. Невозможно передать параметры

Кроме того, существует ES5Object.create()метод нормализует прототипное наследование и может заменитьobjectметод.

6. Паразитическое наследование

Основной точкой: на основе прототипового наследования объект усиливается функцией, которая инкапсулирует процесс наследования, и объект возвращается

function createAnother(original){
  var clone = object(original); // 通过调用 object() 函数创建一个新对象
  clone.sayHi = function(){  // 以某种方式来增强对象
    alert("hi");
  };
  return clone; // 返回这个对象
}

createAnotherОсновная функция функции — добавление свойств и методов в конструктор для улучшения функции.

Недостатки (те же, что и у прототипного наследования):

  1. Значение ссылочного типа в прототипе будет изменено
  2. Невозможно передать параметры

7. Наследование паразитарного состава (рекомендуется)

Этот метод решает в основномнаследование композицииПроблема с двойным вызовом конструктора суперкласса.

function inheritPrototype(sub, super){
  var prototype = Object.create(super.prototype); // 创建对象,父原型的副本
  prototype.constructor = sub;                    // 增强对象
  sub.prototype = prototype;                      // 指定对象,赋给子的原型
}

function staff(){ 
  this.company = "ABC";	
  this.test = [1,2,3];
}
staff.prototype.companyName = function(){
  return this.company; 
}
function employee(name,profession){
  staff.call(this, name);
  this.employeeName = name;
  this.profession = profession;
}

// 将父类原型指向子类
inheritPrototype(employee,staff)
let instanceOne = new employee("Andy", "A");
let instanceTwo = new employee("Rose", "B");
instanceOne.test.push(4);
// 测试 
console.log(instanceTwo.test);            // [1,2,3]
console.log(instanceOne.companyName());   // ABC
// 通过 hasOwnProperty() 方法来确定自身属性与其原型属性
console.log(instanceOne.hasOwnProperty('test'))           // true
// 通过 isPrototypeOf() 方法来确定原型和实例的关系
console.log(staff.prototype.isPrototypeOf(instanceOne));  // true

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

Восемь, наследование классов (рекомендуется)

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

class staff { 
  constructor(){
    this.company = "ABC";	
    this.test = [1,2,3];
  }
  companyName(){
    return this.company; 
  }
}
class employee extends staff {
  constructor(name,profession){
    super();
    this.employeeName = name;
    this.profession = profession;
  }
}

// 将父类原型指向子类
let instanceOne = new employee("Andy", "A");
let instanceTwo = new employee("Rose", "B");
instanceOne.test.push(4);
// 测试 
console.log(instanceTwo.test);    // [1,2,3]
console.log(instanceOne.companyName()); // ABC
// 通过 Object.getPrototypeOf() 方法可以用来从子类上获取父类
console.log(Object.getPrototypeOf(employee) === staff)
// 通过 hasOwnProperty() 方法来确定自身属性与其原型属性
console.log(instanceOne.hasOwnProperty('test'))          // true
// 通过 isPrototypeOf() 方法来确定原型和实例的关系
console.log(staff.prototype.isPrototypeOf(instanceOne));    // true

superключевое слово, которое здесь представляет конструктор родительского класса, который используется для создания нового родительского классаthisобъект.

  1. Подклассы должны быть вconstructorвызов методаsuperметод, иначе будет сообщено об ошибке при создании нового экземпляра. Это потому, что подклассы не имеют своих собственныхthisобъект, но наследует родительский классthisобъект, а затем обработать его.
  2. только звониsuperПосле этого вы можете использоватьthisКлючевые слова, иначе сообщит об ошибке. Это связано с тем, что построение экземпляров подкласса основано на обработке экземпляра родительского класса, толькоsuperметод для возврата экземпляра родительского класса.

Хотя `super` представляет конструктор родительского класса `A`, он возвращает экземпляр подкласса `B`, то есть `this` внутри `super` ссылается на `B`, поэтому `super()` здесь эквивалентно A.prototype.constructor.call(это)

Разница между наследованием реализации ES5 и ES6

Наследование ES5, суть в том, чтобы сначала создать объекты-экземпляры подклассовthis, а затем добавьте метод родительского класса вthisнад(Parent.apply(this)).
Механизм наследования в ES6 совершенно другой, суть в том, чтобы сначала создать экземпляр объекта родительского класса.this(поэтому его нужно назвать первымsuper()метод), а затем используйте конструктор подкласса для измененияthis.

extends наследует основной код (паразитное наследование композиции)

function _inherits(subType, superType) {
  subType.prototype = Object.create(superType && superType.prototype, {
    constructor: {
      value: subType,
      enumerable: false,
      writable: true,
      configurable: true
    }
  });
  if (superType) {
    Object.setPrototypeOf 
    ? Object.setPrototypeOf(subType, superType) 
    : subType.__proto__ = superType;
  }
}

Отсюда видно, что:

  1. подкласс__proto__Свойства, представляющие наследование конструкторов, всегда указывают на родительский класс.
  2. Подклассprototypeатрибут__proto__Атрибуты, представляющие наследование методов, всегда указывают на родительский классprototypeАтрибуты.

Другое: ES6 может настраивать подклассы нативных структур данных (таких как Array, String и т. д.), чего ES5 делать не может.

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

Ссылаться на

  1. Продвинутое программирование на JavaScript
  2. Голодание 6. Жуань Ифэн.com/#docs/class…

постскриптум

Я писала об этом больше двух недель.Главная причина в том,что ребенок только что вошел в мою жизнь.Мне приходится без конца заботиться о ребенке,и тратить много сил и времени на смену подгузников,кормление грудью,переодевание . Эта статья также была написана, пока ребенок спал.Если содержание статьи покажется вам рудиментарным, пожалуйста, потерпите меня и выдвиньте ценные мнения, и я буду пересматривать ее, когда у меня будет время в будущем.

В начале нового года не забудьте изначальный замысел

Серия интерфейсных словарей

Серия «Front-end Dictionary» будет постоянно пополняться, и в каждом выпуске я буду рассказывать о точке знаний, которая появляется все чаще. Надеюсь, что в процессе прочтения в тексте будут неточности или ошибки, буду очень признательна, если что-то почерпну из этой серии, тоже буду очень рада.

Если вы считаете, что мои статьи написаны хорошо, то можете обратить внимание на мой паблик в WeChat, в котором заранее будут раскрыты спойлеры.

Вы также можете добавить мой WeChat wqhhsd, добро пожаловать в общение.

следующее уведомление

[Внешний словарь] Сетевая основа, которую должен понимать внешний интерфейс

портал

  1. [Интерфейсный словарь] Неожиданная удача после разговора с невесткой об агентстве
  2. [Словарь фронта] Решение проблемы проникновения пробивки
  3. Наследование (1) — Вы действительно понимаете цепочку прототипов?
  4. [Внешний словарь] Наследование (2) - Восемь способов написать ответ · Вопросы для интервью