We're looking for a user researcher to understand the needs of developers and designers. Is this you or someone you know? Check out the post: https://mzl.la/2IGzdXS

Перевод не завершен. Пожалуйста, помогите перевести эту статью с английского.

Теперь, когда объясняется большая часть подробностей OOJS, эта статья показывает, как создавать «дочерние» классы объектов (конструкторы), которые наследуют функции из своих «родительских» классов. Кроме того, мы представляем некоторые советы о том, когда и где вы можете использовать OOJS.

Prerequisites: Basic computer literacy, a basic understanding of HTML and CSS, familiarity with JavaScript basics (see First steps and Building blocks) and OOJS basics (see Introduction to objects).
Objective: Чтобы понять, как можно реализовать наследование в JavaScript.

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

До сих пор мы видели некоторое наследование в действии - мы видели, как работают прототипы и как члены наследуются, поднимаясь по цепочке. Но в основном это связано с встроенными функциями браузера. Как создать объект в JavaScript, который наследуется от другого объекта?

Давайте рассмотрим, как это сделать с конкретным примером.

Начало работы

Прежде всего сделайте себе локальную копию нашего файла oojs-class-inheritance-start.html (см. также он работает в режиме реального времени). Внутри здесь вы найдете тот же пример конструктора Person(), который мы использовали весь путь через модуль, с небольшым изменением - мы определили только свойства внутри конструктора:

function Person(first, last, age, gender, interests) {
  this.name = {
    first,
    last
  };
  this.age = age;
  this.gender = gender;
  this.interests = interests;
};

Все методы определены на прототипе конструктора. Например:

Person.prototype.greeting = function() {
  alert('Hi! I\'m ' + this.name.first + '.');
};

Примечание. В исходном коде вы также увидите методы bio() и farewell(). Позже вы увидите, как они могут быть унаследованы другими конструкторами.

Скажем, мы хотели создать класс Teacher, как тот, который мы описали в нашем первоначальном объектно-ориентированном определении, которое наследует всех членов от Person, но также включает в себя:

  1. Новое свойство, subject - это будет содержать предмет, который преподает учитель.
  2. Обновленный метод greeting(), который звучит немного более формально, чем стандартный метод greeting(), более подходит для учителя, обращающегося к некоторым ученикам в школе.

Определение Teacher() функция конструктора

Первое, что нам нужно сделать, это создать конструктор Teacher() - добавьте ниже существующий код:

function Teacher(first, last, age, gender, interests, subject) {
  Person.call(this, first, last, age, gender, interests);

  this.subject = subject;
}

Это похоже на конструктор Person во многих отношениях, но здесь есть что-то странное, что мы не видели раньше - функцию call(). Эта функция в основном позволяет вам вызывать функцию, определенную где-то в другом месте, но в текущем контексте. Первый параметр указывает значение this, которое вы хотите использовать при запуске функции, а остальные параметры - те, которые должны быть переданы функции при ее вызове.

Мы хотим, чтобы конструктор Teacher() принимал те же параметры, что и конструктор Person(), на который он наследуется, поэтому мы указываем их как параметры в вызове call().

Последняя строка внутри конструктора просто определяет новое свойство subject, которое будут иметь учителя, которых нет у общих людей.

В качестве примечания мы могли бы просто сделать это:

function Teacher(first, last, age, gender, interests, subject) {
  this.name = {
    first,
    last
  };
  this.age = age;
  this.gender = gender;
  this.interests = interests;
  this.subject = subject;
}

Но это просто переопределяет свойства заново, а не наследует их от Person(), так что теряется смысл того, что мы пытаемся сделать. Он также занимает больше строк кода.

Наследование от конструктора без параметров

Обратите внимание, что если наследуемый конструктор не принимает значения своего свойства из параметров, вам не нужно указывать их в качестве дополнительных аргументов в call(). Так, например, если у вас было что-то действительно простое:

function Brick() {
  this.width = 10;
  this.height = 20;
}

Вы можете наследовать свойства width и height, выполнив это (как и другие шаги, описанные ниже, конечно):

function BlueGlassBrick() {
  Brick.call(this);

  this.opacity = 0.5;
  this.color = 'blue';
}

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

Установка Teacher()'s prototype и конструктор ссылок

Пока все хорошо, но у нас есть проблема. Мы определили новый конструктор и у него есть свойство prototype, которое по умолчанию просто содержит ссылку на саму конструкторскую функцию. Он не содержит методов свойства prototype конструктора Person. Чтобы увидеть это, введите Object.getOwnPropertyNames(Teacher.prototype) в поле ввода текста или в вашу консоль JavaScript. Затем введите его снова, заменив Teacher Person. И новый конструктор не наследует эти методы. Чтобы увидеть это, сравните выходы Person.prototype.greeting и Teacher.prototype.greeting. Нам нужно заставить Teacher() наследовать методы, определенные на прототипе Person(). Итак, как мы это делаем?

  1. Добавьте следующую строку ниже своего предыдущего добавления:
    Teacher.prototype = Object.create(Person.prototype);
    Здесь наш друг create() снова приходит на помощь. В этом случае мы используем его для создания нового объекта и делаем его значением Teacher.prototype. Новый объект имеет свой прототип Person.prototype и, следовательно, наследует, если и когда это необходимо, все доступные методы Person.prototype.
  2. Нам нужно сделать еще одну вещь, прежде чем двигаться дальше. После добавления последней строки свойство constructor Teacher.prototype теперь равно Person(), потому что мы просто устанавливаем Teacher.prototype для ссылки на объект, который наследует его свойства от Person.prototype! Попробуйте сохранить код, загрузите страницу в браузере и введите Teacher.prototype.constructor в консоль для проверки.
  3. Это может стать проблемой, поэтому нам нужно установить это право. Вы можете сделать это, вернувшись к исходному коду и добавив следующую строку внизу:
    Teacher.prototype.constructor = Teacher;
  4. Теперь, если вы сохраните и обновите, введите Teacher.prototype.constructor, чтобы вернуть Teacher(), если хотите, плюс мы теперь наследуем Person()!

Предоставление Teacher() новой функции greeting()

Чтобы завершить наш код, нам нужно определить новую функцию greeting() в конструкторе Teacher().

Самый простой способ сделать это - определить его на прототипе Teacher() - добавить в нижнюю часть кода следующее:

Teacher.prototype.greeting = function() {
  var prefix;

  if (this.gender === 'male' || this.gender === 'Male' || this.gender === 'm' || this.gender === 'M') {
    prefix = 'Mr.';
  } else if (this.gender === 'female' || this.gender === 'Female' || this.gender === 'f' || this.gender === 'F') {
    prefix = 'Mrs.';
  } else {
    prefix = 'Mx.';
  }

  alert('Hello. My name is ' + prefix + ' ' + this.name.last + ', and I teach ' + this.subject + '.');
};

Это alert приветствие учителя, которое также использует соответствующий префикс имени для своего пола, разработанный с использованием условного оператора.

Попробуйте пример

Теперь, когда вы ввели весь код, попробуйте создать экземпляр объекта из Teacher(), поставив ниже внизу вашего JavaScript (или что-то похожее по вашему выбору):

var teacher1 = new Teacher('Dave', 'Griffiths', 31, 'male', ['football', 'cookery'], 'mathematics');

Теперь сохраните и обновите и попробуйте получить доступ к свойствам и методам вашего нового объекта teacher1, например:

teacher1.name.first;
teacher1.interests[0];
teacher1.bio();
teacher1.subject;
teacher1.greeting();
teacher1.farewell();

Все должно работать нормально. Запросы в строках 1, 2, 3 и 6 членов доступа, унаследованных от общего конструктора Person() (класса). Запрос в строке 4 обращается к члену, доступному только для более специализированного конструктора (класса) Teacher(). Запрос в строке 5 имел бы доступ к члену, унаследованному от Person(), за исключением того факта, что Teacher() имеет свой собственный член с тем же именем, поэтому запрос обращается к этому члену.

Примечание. Если вам не удается заставить это работать, сравните свой код с нашей готовой версией (см. также живую трансляцию).

Методика, которую мы здесь рассмотрили, - это не единственный способ создания наследующих классов в JavaScript, но он работает нормально и это дает вам представление о том, как реализовать наследование в JavaScript.

Вам также может быть интересно узнать некоторые из новых функций ECMAScript, которые позволяют нам делать наследование более чисто в JavaScript (см. Classes). Мы не рассматривали их здесь, поскольку они пока не поддерживаются очень широко в браузерах. Все остальные конструкторы кода, которые мы обсуждали в этом наборе статей, поддерживаются еще в IE9 или ранее и есть способы добиться более ранней поддержки, чем это.

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

Дальнейшее упражнение

В нашем OOP theory section мы также включили класс Student как концепцию, которая наследует все особенности Person, а также имеет другой метод greeting() от Person, который гораздо более неформален, чем приветствие Teacher. Посмотрите, как выглядит приветствие ученика в этом разделе, и попробуйте реализовать собственный конструктор Student(), который наследует все функции Person() и реализует другую функцию greeting().

Note: If you have trouble getting this to work, have a look at our finished version (see it running live also).

Object member summary

Подводя итог, вы в основном получили три типа свойств / методов, о которых нужно беспокоиться:

  1. Те, которые определены внутри функции-конструктора, которые присваиваются экземплярам объекта. Их довольно легко заметить - в вашем собственном коде они представляют собой элементы, определенные внутри конструктора, используя строки this.x = x; в встроенном коде браузера они являются членами, доступными только для экземпляров объектов (обычно создаются путем вызова конструктора с использованием ключевого слова new, например var myInstance = new myConstructor ().
  2. Те, которые определяются непосредственно самим конструктором, которые доступны только для конструктора. Они обычно доступны только для встроенных объектов браузера и распознаются путем непосредственной привязки к конструктору, а не к экземпляру. Например, Object.keys().
  3. Те, которые определены в прототипе конструктора, которые наследуются всеми экземплярами и наследуют классы объектов. К ним относятся любой член, определенный в свойстве прототипа конструктора, например. myConstructor.prototype.x().

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

Когда вы используете наследование в JavaScript?

В частности, после этой последней статьи вы можете подумать: «Woo, это сложно». Ну, ты прав. Прототипы и наследование представляют собой некоторые из самых сложных аспектов JavaScript, но многие возможности и гибкость JavaScript вытекают из его структуры объектов и наследования и стоит понять, как это работает.

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

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

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

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

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

Резюме

В этой статье мы рассмотрели оставшуюся часть основной теории и синтаксиса OOJS, которые, как мы думаем, вам следует знать сейчас. На этом этапе вы должны понимать основы JavaScript, ООП, прототипы и прототипное наследование, как создавать классы (конструкторы) и экземпляры объектов, добавлять функции в классы и создавать подклассы, которые наследуются от других классов.

В следующей статье мы рассмотрим, как работать с JavaScript Object Notation (JSON), общим форматом обмена данными, написанным с использованием объектов JavaScript.

See also

  • ObjectPlayground.com — A really useful interactive learning site for learning about objects.
  • Secrets of the JavaScript Ninja, Chapter 6 — A good book on advanced JavaScript concepts and techniques, by John Resig and Bear Bibeault. Chapter 6 covers aspects of prototypes and inheritance really well; you can probably track down a print or online copy fairly easily.
  • You Don't Know JS: this & Object Prototypes — Part of Kyle Simpson's excellent series of JavaScript manuals, Chapter 5 in particular looks at prototypes in much more detail than we do here. We've presented a simplified view in this series of articles aimed at beginners, whereas Kyle goes into great depth and provides a more complex but more accurate picture.

 

In this module

 

Метки документа и участники

Внесли вклад в эту страницу: slychai85
Обновлялась последний раз: slychai85,