diff options
Diffstat (limited to 'files/ru/learn/javascript/objects/classes_in_javascript/index.html')
-rw-r--r-- | files/ru/learn/javascript/objects/classes_in_javascript/index.html | 267 |
1 files changed, 267 insertions, 0 deletions
diff --git a/files/ru/learn/javascript/objects/classes_in_javascript/index.html b/files/ru/learn/javascript/objects/classes_in_javascript/index.html new file mode 100644 index 0000000000..ec27663266 --- /dev/null +++ b/files/ru/learn/javascript/objects/classes_in_javascript/index.html @@ -0,0 +1,267 @@ +--- +title: Наследование в JavaScript +slug: Learn/JavaScript/Objects/Classes_in_JavaScript +tags: + - JavaScript + - Наследование + - ООП +translation_of: Learn/JavaScript/Objects/Inheritance +original_slug: Learn/JavaScript/Objects/Inheritance +--- +<p> + <audio class="audio-for-speech"></audio> +</p> + +<div class="translate-tooltip-mtz hidden"> +<div class="header"> +<div class="header-controls"></div> + +<div class="translate-icons"><img class="from" src=""> <img class="arrow"> <img class="to" src=""></div> +</div> + +<div class="translated-text"> +<div class="words"></div> + +<div class="sentences"></div> +</div> +</div> + +<div>{{LearnSidebar}}</div> + +<div>{{PreviousMenuNext("Learn/JavaScript/Objects/Object_prototypes", "Learn/JavaScript/Objects/JSON", "Learn/JavaScript/Objects")}}</div> + +<p class="summary">Теперь, когда объясняется большая часть подробностей OOJS, эта статья показывает, как создавать «дочерние» классы объектов (конструкторы), которые наследуют признаки из своих «родительских» классов. Кроме того, мы дадим некоторые советы о том, когда и где вы можете использовать OOJS , и посмотрим, как классы рассматриваются в современном синтаксисе ECMAScript.</p> + +<table class="learn-box standard-table"> + <tbody> + <tr> + <th scope="row">Необходимые знания:</th> + <td> + <p>Базовая компьютерная грамотность, понимание основ HTML и CSS, знакомство с основами JavaScript (см. <a href="https://developer.mozilla.org/ru/docs/Learn/JavaScript/%D0%9F%D0%B5%D1%80%D0%B2%D1%8B%D0%B5_%D1%88%D0%B0%D0%B3%D0%B8">Первые шаги</a> и <a href="https://developer.mozilla.org/ru/docs/Learn/JavaScript/Building_blocks">Структурные элементы</a>) and основы Объектно-ориентированного JS (см. <a href="https://developer.mozilla.org/ru/docs/Learn/JavaScript/%D0%9E%D0%B1%D1%8A%D0%B5%D0%BA%D1%82%D1%8B/%D0%9E%D1%81%D0%BD%D0%BE%D0%B2%D1%8B">Введение в объекты</a>).</p> + </td> + </tr> + <tr> + <th scope="row">Цель:</th> + <td>Понять, как можно реализовать наследование в JavaScript.</td> + </tr> + </tbody> +</table> + +<h2 id="Прототипное_наследование">Прототипное наследование</h2> + +<p>До сих пор мы видели некоторое наследование в действии - мы видели, как работают прототипы и как элементы наследуются, поднимаясь по цепочке. Но в основном это связано с встроенными функциями браузера. Как создать объект в JavaScript, который наследует от другого объекта?</p> + +<p>Давайте рассмотрим, как это сделать на конкретном примере.</p> + +<h2 id="Начало_работы">Начало работы</h2> + +<p>Прежде всего сделайте себе локальную копию нашего файла <a href="https://github.com/mdn/learning-area/blob/master/javascript/oojs/advanced/oojs-class-inheritance-start.html">oojs-class-inheritance-start.html</a> (он также работает <a href="https://mdn.github.io/learning-area/javascript/oojs/advanced/oojs-class-inheritance-start.html">в режиме реального времени</a>). В файле вы найдёте тот же пример конструктора <code>Person()</code>, который мы использовали на протяжении всего модуля, с небольшим отличием - мы определили внутри конструктора только лишь свойства:</p> + +<pre class="brush: js">function Person(first, last, age, gender, interests) { + this.name = { + first, + last + }; + this.age = age; + this.gender = gender; + this.interests = interests; +};</pre> + +<p><em>Все</em> методы определены в прототипе конструктора. Например:</p> + +<pre class="brush: js">Person.prototype.greeting = function() { + alert('Hi! I\'m ' + this.name.first + '.'); +};</pre> + +<div class="note"> +<p><strong>Примечание</strong>. В исходном коде вы также увидите определённые методы <code>bio()</code> и <code>farewell()</code>. Позже вы увидите, как они могут быть унаследованы другими конструкторами.</p> +</div> + +<p>Скажем так, мы хотели создать класс <code>Teacher</code>, подобный тому, который мы описали в нашем первоначальном объектно-ориентированном определении, которое наследует всех членов от <code>Person</code>, но также включает в себя:</p> + +<ol> + <li>Новое свойство, <code>subject</code> - оно будет содержать предмет, который преподаёт учитель.</li> + <li>Обновлённый метод <code>greeting()</code>, который звучит немного более формально, чем стандартный метод <code>greeting()</code>— более подходит для учителя, обращающегося к некоторым ученикам в школе.</li> +</ol> + +<h2 id="Определение_функции-конструктора_Teacher">Определение функции-конструктора Teacher()</h2> + +<p>Первое, что нам нужно сделать, это создать конструктор <code>Teacher()</code> - добавьте ниже следующий код:</p> + +<pre class="brush: js">function Teacher(first, last, age, gender, interests, subject) { + Person.call(this, first, last, age, gender, interests); + + this.subject = subject; +}</pre> + +<p>Это похоже на конструктор Person во многих отношениях, но здесь есть что-то странное, что мы не видели раньше - функцию <code><a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/call">call()</a></code>. Эта функция в основном позволяет вам вызывать функцию, определённую где-то в другом месте, но в текущем контексте. Первый параметр указывает значение <code><a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/this">this</a></code>, которое вы хотите использовать при выполнении функции, а остальные параметры - те, которые должны быть переданы функции при её вызове.</p> + +<p>Мы хотим, чтобы конструктор <code>Teacher()</code> принимал те же параметры, что и конструктор <code>Person()</code>, от которого он наследуется, поэтому мы указываем их как параметры в вызове <code>call()</code>.</p> + +<p>Последняя строка внутри конструктора просто определяет новое свойство <code>subject</code>, которое будут иметь учителя, и которого нет у Person().</p> + +<p>В качестве примечания мы могли бы просто сделать это:</p> + +<pre class="brush: js">function Teacher(first, last, age, gender, interests, subject) { + this.name = { + first, + last + }; + this.age = age; + this.gender = gender; + this.interests = interests; + this.subject = subject; +}</pre> + +<p>Но это просто переопределяет свойства заново, а не наследует их от <code>Person()</code>, так что теряется смысл того, что мы пытаемся сделать. Он также занимает больше строк кода.</p> + +<h3 id="Наследование_от_конструктора_без_параметров">Наследование от конструктора без параметров</h3> + +<p>Обратите внимание, что если конструктор, от которого вы наследуете, не принимает значения своего свойства из параметров, вам не нужно указывать их в качестве дополнительных аргументов в <code>call()</code>. Так, например, если у вас было что-то действительно простое:</p> + +<pre class="brush: js">function Brick() { + this.width = 10; + this.height = 20; +}</pre> + +<p>Вы можете наследовать свойства <code>width</code> и <code>height</code>, выполнив это (как и другие шаги, описанные ниже, конечно):</p> + +<pre class="brush: js">function BlueGlassBrick() { + Brick.call(this); + + this.opacity = 0.5; + this.color = 'blue'; +}</pre> + +<p>Обратите внимание, что мы указали только <code>this</code> внутри <code>call()</code> - никаких других параметров не требуется, поскольку мы не наследуем никаких свойств родителя, которые задаются через параметры.</p> + +<h2 id="Установка_Teachers_prototype_и_конструктор_ссылок">Установка Teacher()'s prototype и конструктор ссылок</h2> + +<p>Пока все хорошо, но у нас есть проблема. Мы определили новый конструктор и у него есть свойство <code>prototype</code>, которое по умолчанию просто содержит ссылку на саму конструкторскую функцию. Он не содержит методов свойства <code>prototype</code> конструктора <code>Person</code>. Чтобы увидеть это, введите <code>Object.getOwnPropertyNames(Teacher.prototype)</code> в поле ввода текста или в вашу консоль JavaScript. Затем введите его снова, заменив <code>Teacher</code> на <code>Person</code>. Новый конструктор <em>не наследует</em> эти методы. Чтобы увидеть это, сравните выводы в консоль <code>Person.prototype.greeting</code> и <code>Teacher.prototype.greeting</code>. Нам нужно заставить <code>Teacher()</code> наследовать методы, определённые на прототипе <code>Person()</code>. Итак, как мы это делаем?</p> + +<ol> + <li>Добавьте следующую строку ниже своего предыдущего добавления: + <pre class="brush: js">Teacher.prototype = Object.create(Person.prototype);</pre> + Здесь наш друг <code><a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/create">create()</a></code> снова приходит на помощь. В этом случае мы используем его для создания нового объекта и делаем его значением <code>Teacher.prototype</code>. Новый объект имеет свой прототип <code>Person.prototype</code> и, следовательно, наследует, если и когда это необходимо, все доступные методы <code>Person.prototype</code>.</li> + <li>Нам нужно сделать ещё одну вещь, прежде чем двигаться дальше. После добавления последней строки, <code>Teacher.prototype.constructor</code> стало равным <code>Person()</code>, потому что мы просто устанавливаем <code>Teacher.prototype</code> для ссылки на объект, который наследует его свойства от <code>Person.prototype</code>! Попробуйте сохранить код, загрузите страницу в браузере и введите <code>Teacher.prototype.constructor</code> в консоль для проверки.</li> + <li>Это может стать проблемой, поэтому нам нужно сделать это правильно. Вы можете сделать это, вернувшись к исходному коду и добавив следующие строки внизу: + <pre><code>Object.defineProperty(Teacher.prototype, 'constructor', { + value: Teacher, + enumerable: false, // false, чтобы данное свойство не появлялось в цикле for in + writable: true });</code></pre> + </li> + <li>Теперь, если вы сохраните и обновите, введите <code>Teacher.prototype.constructor</code>, чтобы вернуть <code>Teacher()</code>, плюс мы теперь наследуем <code>Person()</code>!</li> +</ol> + +<h2 id="Предоставление_Teacher_новой_функции_greeting">Предоставление Teacher() новой функции greeting()</h2> + +<p>Чтобы завершить наш код, нам нужно определить новую функцию <code>greeting()</code> в конструкторе <code>Teacher()</code>.</p> + +<p>Самый простой способ сделать это - определить его на прототипе <code>Teacher()</code> - добавить в нижнюю часть кода следующее:</p> + +<pre class="brush: js">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 + '.'); +};</pre> + +<p>Это выводит на экран приветствие учителя, в котором используется соответствующий префикс имени для своего пола, разработанный с использованием условного оператора.</p> + +<h2 id="Попробуйте_пример">Попробуйте пример</h2> + +<p>Теперь, когда вы ввели весь код, попробуйте создать экземпляр объекта из <code>Teacher()</code>, поставив ниже вашего JavaScript-кода (или что-то похожее по вашему выбору):</p> + +<pre class="brush: js">var teacher1 = new Teacher('Dave', 'Griffiths', 31, 'male', ['football', 'cookery'], 'mathematics');</pre> + +<p>Теперь сохраните, обновите, и попробуйте получить доступ к свойствам и методам вашего нового объекта <code>teacher1</code>, например:</p> + +<pre class="brush: js">teacher1.name.first; +teacher1.interests[0]; +teacher1.bio(); +teacher1.subject; +teacher1.greeting(); +teacher1.farewell();</pre> + +<p>Все должно работать нормально. Запросы в строках 1, 2, 3 и 6 унаследованные от общего конструктора <code>Person()</code> (класса). Запрос в строке 4 обращается к <code>subject</code>, доступному только для более специализированного конструктора (класса) <code>Teacher()</code>. Запрос в строке 5 получил бы доступ к методу <code>greeting()</code>, унаследованному от <code>Person()</code>, но <code>Teacher()</code> имеет свой собственный метод <code>greeting()</code> с тем же именем, поэтому запрос обращается к этому методу.</p> + +<div class="note"> +<p><strong>Примечание</strong>. Если вам не удаётся заставить это работать, сравните свой код с нашей <a href="https://github.com/mdn/learning-area/blob/master/javascript/oojs/advanced/oojs-class-inheritance-finished.html">готовой версией</a> (см. также <a href="http://mdn.github.io/learning-area/javascript/oojs/advanced/oojs-class-inheritance-student.html">рабочее демо</a>).</p> +</div> + +<p>Методика, которую мы здесь рассмотрели, - это не единственный способ создания наследующих классов в JavaScript, но он работает нормально и это даёт вам представление о том, как реализовать наследование в JavaScript.</p> + +<p>Вам также может быть интересно узнать некоторые из новых функций {{glossary("ECMAScript")}}, которые позволяют нам делать наследование более чисто в JavaScript (см. <a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Classes">Classes</a>). Мы не рассматривали их здесь, поскольку они пока не поддерживаются очень широко в браузерах. Все остальные конструкторы кода, которые мы обсуждали в этом наборе статей, поддерживаются ещё в IE9 или ранее и есть способы добиться более ранней поддержки, чем это.</p> + +<p>Обычный способ - использовать библиотеку JavaScript - большинство популярных опций имеют простой набор функций, доступных для выполнения наследования более легко и быстро. <a href="http://coffeescript.org/#classes">CoffeeScript</a> , например, предоставляет класс, расширяет и т.д.</p> + +<h2 id="Дальнейшее_упражнение">Дальнейшее упражнение</h2> + +<p>В нашем <a href="https://developer.mozilla.org/en-US/docs/Learn/JavaScript/Objects/Object-oriented_JS#Object-oriented_programming_from_10000_meters">руководстве по Объектно-ориентированному JavaScript для начинающих</a> мы также включили класс <code>Student</code> как концепцию, которая наследует все особенности <code>Person</code>, а также имеет другой метод <code>greeting()</code> от <code>Person</code>, который гораздо более неформален, чем приветствие <code>Teacher</code>. Посмотрите, как выглядит приветствие ученика в этом разделе, и попробуйте реализовать собственный конструктор <code>Student()</code>, который наследует все функции <code>Person()</code> и реализует другую функцию <code>greeting()</code>.</p> + +<div class="note"> +<p><strong>Примечание</strong>. Если вам не удаётся заставить это работать, сравните свой код с нашей <a href="https://github.com/mdn/learning-area/blob/master/javascript/oojs/advanced/oojs-class-inheritance-student.html">готовой версией</a> (см. также <a href="http://mdn.github.io/learning-area/javascript/oojs/advanced/oojs-class-inheritance-student.html">рабочее демо</a>).</p> +</div> + +<h2 id="Object_member_summary">Object member summary</h2> + +<p>Подводя итог, вы в основном получили три типа свойств / методов, о которых нужно беспокоиться:</p> + +<ol> + <li>Те, которые определены внутри функции-конструктора, которые присваиваются экземплярам объекта. Их довольно легко заметить - в вашем собственном коде они представляют собой элементы, определённые внутри конструктора, используя строки <code>this.x = x</code>; в встроенном коде браузера они являются членами, доступными только для экземпляров объектов (обычно создаются путём вызова конструктора с использованием ключевого слова <code>new</code>, например <code>var myInstance = new myConstructor ()</code>.</li> + <li>Те, которые определяются непосредственно самим конструктором, которые доступны только для конструктора. Они обычно доступны только для встроенных объектов браузера и распознаются путём непосредственной привязки к конструктору, а не к экземпляру. Например, <code><a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/keys">Object.keys()</a></code>.</li> + <li>Те, которые определены в прототипе конструктора, которые наследуются всеми экземплярами и наследуют классы объектов. К ним относятся любой член, определённый в свойстве прототипа конструктора, например. <code>myConstructor.prototype.x()</code>.</li> +</ol> + +<p>Если вы не уверены, что это такое, не беспокойтесь об этом, пока вы ещё учитесь и знание придёт с практикой.</p> + +<h2 id="Когда_вы_используете_наследование_в_JavaScript">Когда вы используете наследование в JavaScript?</h2> + +<p>В частности, после этой последней статьи вы можете подумать: «У-у-у, это сложно». Ну, ты прав. Прототипы и наследование представляют собой некоторые из самых сложных аспектов JavaScript, но многие возможности и гибкость JavaScript вытекают из его структуры объектов и наследования и стоит понять, как это работает.</p> + +<p>В некотором смысле вы используете наследование все время. Всякий раз, когда вы используете различные функции веб-API или методы/свойства, определённые во встроенном объекте браузера, который вы вызываете в своих строках, массивах и т.д., вы неявно используете наследование.</p> + +<p>Что касается использования наследования в вашем собственном коде, вы, вероятно, не будете часто его использовать, особенно для начала и в небольших проектах. Это пустая трата времени на использование объектов и наследование только ради этого, когда они вам не нужны. Но по мере того, как ваши базы кода становятся больше, вы с большей вероятностью найдёте необходимость в этом. Если вы начинаете создавать несколько объектов с подобными функциями, то создание универсального типа объекта, содержащего все общие функции и наследование этих функций в более специализированных типах объектов, может быть удобным и полезным.</p> + +<div class="note"> +<p><strong>Примечание</strong>. Из-за того, как работает JavaScript, с цепочкой прототипов и т.д., совместное использование функций между объектами часто называется <strong>делегированием</strong>. Специализированные объекты делегируют функциональность универсальному типу объекта.</p> +</div> + +<p>При использовании наследования вам рекомендуется не иметь слишком много уровней наследования и тщательно отслеживать, где вы определяете свои методы и свойства. Можно начать писать код, который временно изменяет прототипы встроенных объектов браузера, но вы не должны этого делать, если у вас нет действительно веской причины. Слишком много наследования могут привести к бесконечной путанице и бесконечной боли при попытке отладки такого кода.</p> + +<p>В конечном счёте, объекты - это ещё одна форма повторного использования кода, например функций или циклов, со своими конкретными ролями и преимуществами. Если вы обнаруживаете, что создаёте кучу связанных переменных и функций и хотите отслеживать их все вместе и аккуратно их упаковывать, объект является хорошей идеей. Объекты также очень полезны, когда вы хотите передать коллекцию данных из одного места в другое. Обе эти вещи могут быть достигнуты без использования конструкторов или наследования. Если вам нужен только один экземпляр объекта, вам лучше всего использовать литерал объекта и вам, разумеется, не нужно наследование.</p> + +<h2 id="Резюме">Резюме</h2> + +<p>В этой статье мы рассмотрели оставшуюся часть основной теории и синтаксиса OOJS, которые, как мы думаем, вам следует знать сейчас. На этом этапе вы должны понимать основы JavaScript, ООП, прототипы и прототипное наследование, как создавать классы (конструкторы) и экземпляры объектов, добавлять функции в классы и создавать подклассы, которые наследуются от других классов.</p> + +<p>В следующей статье мы рассмотрим, как работать с JavaScript Object Notation (JSON), общим форматом обмена данными, написанным с использованием объектов JavaScript.</p> + +<h2 id="See_also">See also</h2> + +<ul> + <li><a href="http://www.objectplayground.com/">ObjectPlayground.com</a> — A really useful interactive learning site for learning about objects.</li> + <li><a href="https://www.amazon.com/gp/product/193398869X/">Secrets of the JavaScript Ninja</a>, 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.</li> + <li><a href="https://github.com/getify/You-Dont-Know-JS/blob/master/this%20&%20object%20prototypes/README.md#you-dont-know-js-this--object-prototypes">You Don't Know JS: this & Object Prototypes</a> — 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.</li> +</ul> + +<p>{{PreviousMenuNext("Learn/JavaScript/Objects/Object_prototypes", "Learn/JavaScript/Objects/JSON", "Learn/JavaScript/Objects")}}</p> + +<h2 id="В_этом_модуле">В этом модуле</h2> + +<ul> + <li><a href="https://developer.mozilla.org/ru/docs/Learn/JavaScript/Объекты/Основы">Основы объекта</a></li> + <li><a href="https://developer.mozilla.org/ru/docs/Learn/JavaScript/Объекты/Object-oriented_JS">Объектно-ориентированный JavaScript для начинающих</a></li> + <li><a href="https://developer.mozilla.org/ru/docs/Learn/JavaScript/Объекты/Object_prototypes">Прототипы объектов</a></li> + <li><a href="https://developer.mozilla.org/ru/docs/Learn/JavaScript/Объекты/Inheritance">Наследование в JavaScript</a></li> + <li><a href="https://developer.mozilla.org/ru/docs/Learn/JavaScript/Объекты/JSON">Работа с данными JSON</a></li> + <li><a href="https://developer.mozilla.org/ru/docs/Learn/JavaScript/Объекты/Object_building_practice">Практика построения объектов</a></li> + <li><a href="https://developer.mozilla.org/ru/docs/Learn/JavaScript/Объекты/Adding_bouncing_balls_features">Добавление функций в нашу демонстрацию прыгающих шаров</a></li> +</ul> |