aboutsummaryrefslogtreecommitdiff
path: root/files/ru/web/javascript/guide/details_of_the_object_model
diff options
context:
space:
mode:
Diffstat (limited to 'files/ru/web/javascript/guide/details_of_the_object_model')
-rw-r--r--files/ru/web/javascript/guide/details_of_the_object_model/index.html723
1 files changed, 723 insertions, 0 deletions
diff --git a/files/ru/web/javascript/guide/details_of_the_object_model/index.html b/files/ru/web/javascript/guide/details_of_the_object_model/index.html
new file mode 100644
index 0000000000..a5fda463eb
--- /dev/null
+++ b/files/ru/web/javascript/guide/details_of_the_object_model/index.html
@@ -0,0 +1,723 @@
+---
+title: Подробнее об объектной модели
+slug: Web/JavaScript/Guide/Details_of_the_Object_Model
+translation_of: Web/JavaScript/Guide/Details_of_the_Object_Model
+---
+<p>{{jsSidebar("JavaScript Guide")}} {{PreviousNext("Web/JavaScript/Guide/Working_with_Objects", "Web/JavaScript/Guide/Ispolzovanie_promisov")}}</p>
+
+<p class="summary">JavaScript — это объектно-ориентированный язык, основанный на прототипировании, а не на классах. Из-за этого, менее очевидно то, каким образом JavaScript позволяет создавать иерархии объектов и обеспечивает наследование свойств и их значений. Эта глава является скромной попыткой прояснить ситуацию.</p>
+
+<p>Эта глава предполагает что читатель знаком с основами JavaScript, и имеет опыт использования функций для создания простейших объектов.</p>
+
+<h2 id="Языки_основанные_на_классах_против_Прототипно-ориентированных_языков">Языки, основанные на классах против Прототипно-ориентированных языков</h2>
+
+<p>Основанные на классах объектно-ориентированные языки программирования, такие как Java и C++, строятся на концепции двух отдельных сущностей: класс и экземпляр.</p>
+
+<ul>
+ <li><em>Класс</em> определяет все свойства (учитывая методы и все поля в  Java, или свойства в C++), которые характеризуют группу объектов. Класс это абстрактная вещь, а не какой-либо конкретный член множества объектов, которые он описывает. Например, класс <code><span style="font-family: consolas,monaco,andale mono,monospace;">Employee</span></code> может описывать множество всех сотрудников.</li>
+ <li><em>Экземпляр</em>, это воплощение класса в виде конкретного объекта. Например, <font face="Consolas, Liberation Mono, Courier, monospace"><code>Victoria</code> </font>может быть экземпляром класса <code><span style="font-family: consolas,monaco,andale mono,monospace;">Employee</span></code>, представляющий собой конкретного сотрудника. Экземпляр класса имеет ровно столько свойств, сколько и родительский класс (не больше и не меньше).</li>
+</ul>
+
+<p>Прототипно-ориентированный язык, например JavaScript, не реализует данное различие: он имеет только объекты. Языки, основанные на прототипах, имеют понятие <em>прототипа объекта — </em>это<em> </em>объект, используемый в качестве шаблона, с целью получить изначальные свойства для нового объекта. Любой объект может иметь собственные свойства, присвоенные либо во время создания, либо во время выполнения. В дополнение, любой объект может быть указан в качестве <em>прототипа </em>для другого объекта, это позволит второму объекту использовать свойства первого.</p>
+
+<h3 id="Определение_класса">Определение класса</h3>
+
+<p>В классо-ориентированных языках, вы можете <em>определить класс</em>. В этом определении вы можете указать специальные методы, называемые <em>конструкторами</em>, которые позволят создать экземпляр класса. Метод конструктор может задать начальные значения для свойств экземпляра и выполнять другие действия, в момент создания. Вы можете использовать оператор  <code>new</code>, совместно с методом конструктора, для создания экземпляров классов.</p>
+
+<p>JavaScript использует похожую модель, но не имеет определения класса отдельно от конструктора. Вместо этого, вы определяете функцию-конструктор для создания объектов с начальным набором свойств и значений. Любая функция в JavaScript может быть использована, как конструктор. Вы должны использовать оператор <code>new</code> для создания нового объекта.</p>
+
+<h3 id="Подклассы_и_наследование">Подклассы и наследование</h3>
+
+<p>В языках, основанных на классах, вы создаете иерархию классов через объявление классов. В объявлении класса вы можете указать, что новый класс является <em>подклассом</em> уже существующего класса. При этом, подкласс унаследует все свойства суперкласса и в дополнение сможет добавить свои свойства или переопределить унаследованные. Например, предположим, что класс <code><span style="font-family: consolas,monaco,andale mono,monospace;">Employee</span></code> включает два свойства: <code>name</code> и <code>dept</code>, а класс <code><span style="font-family: consolas,monaco,andale mono,monospace;">Manager</span></code> является подклассом <code><span style="font-family: consolas,monaco,andale mono,monospace;">Employee</span></code> и добавляет свойство <code>reports</code>. В этом случае, экземпляр класса <code><span style="font-family: consolas,monaco,andale mono,monospace;">Manager</span></code> будет иметь три свойства: <code>name</code>, <code>dept</code>, и <code>reports</code>.</p>
+
+<p>JavaScript реализует наследование, позволяя связать прототипный объект с любой функцией-конструктором. Итак, вы можете создать объект точь-в-точь, как в примере <code><span style="font-family: consolas,monaco,andale mono,monospace;">Employee</span></code> — <code><span style="font-family: consolas,monaco,andale mono,monospace;">Manager</span></code>, но используя несколько иную технику. Для начала нужно определить функцию-конструктор <code><span style="font-family: consolas,monaco,andale mono,monospace;">Employee</span></code>, которая определяет свойства <code>name</code> и <code>dept</code>. Затем, определяем функцию-конструктор <code><span style="font-family: consolas,monaco,andale mono,monospace;">Manager</span></code>, в которой в свою очередь, будет явно вызываться конструктор <span style="font-family: consolas,monaco,andale mono,monospace;"><code>Employee</code> и</span> определяться новое свойство <code>reports</code>. Наконец, присваиваем новый экземпляр <code><span style="font-family: consolas,monaco,andale mono,monospace;">Employee</span></code>, в качестве <code>prototype</code> для функции-конструктора <code><span style="font-family: consolas,monaco,andale mono,monospace;">Manager</span></code>. Теперь, когда вы создадите нового <span style="font-family: consolas,monaco,andale mono,monospace;">Manager</span>, он унаследует свойства <font face="consolas, Liberation Mono, courier, monospace"><span style="background-color: rgba(220, 220, 220, 0.5);">name</span></font> и <font face="consolas, Liberation Mono, courier, monospace"><span style="background-color: rgba(220, 220, 220, 0.5);">dept</span></font> из объекта <code><span style="font-family: consolas,monaco,andale mono,monospace;">Employee</span></code>.</p>
+
+<h3 id="Добавление_и_удаление_свойств">Добавление и удаление свойств</h3>
+
+<p>В языках, основанных на классах, вы, как правило, создаете класс во время компиляции, а затем вы создаёте экземпляры класса либо во время компиляции, либо во время выполнения. Вы не можете изменить количество или тип свойств класса после определения класса. В JavaScript, однако, вы можете добавлять или удалять свойства любого объекта. Если вы добавляете свойство к объекту, который используется в качестве прототипа для множества объектов, то все эти объекты, для которых он является прототипом, также получат это свойство.</p>
+
+<h3 id="Подытожим_различия">Подытожим различия</h3>
+
+<p>Следующая таблица дает краткий обзор некоторых из этих различий. А оставшаяся часть этой главы описывает детали использования конструкторов и прототипов JavaScript для создания иерархии объектов и сравнивает это с тем, как вы могли бы сделать это в Java.</p>
+
+<table class="fullwidth-table">
+ <caption>Сравнение языков на основе классов (Java) и на базе прототипов (JavaScript)</caption>
+ <thead>
+ <tr>
+ <th scope="col">Основанные на классах (Java)</th>
+ <th scope="col">Основанные на базе прототипов (JavaScript)</th>
+ </tr>
+ </thead>
+ <tbody>
+ <tr>
+ <td>Класс и экземпляр являются разными сущностями.</td>
+ <td>Все объекты могут наследовать свойства другого объекта.</td>
+ </tr>
+ <tr>
+ <td>Определяем класс с помощью определения класса; создаем экземпляр класса с помощью метода-конструктора.</td>
+ <td>Определение и создание объекта происходит с помощью функций-конcтрукторов.</td>
+ </tr>
+ <tr>
+ <td>Создание отдельного объекта с помощью оператора <code>new</code>.</td>
+ <td>Так же.</td>
+ </tr>
+ <tr>
+ <td>Иерархия объектов строится с помощью определения классов и их подклассов.</td>
+ <td>
+ <p>Построение иерархии объектов происходит путем присвоения объекта в качестве прототипа функции-конструктора.</p>
+ </td>
+ </tr>
+ <tr>
+ <td>Наследование свойств в цепочке классов.</td>
+ <td>Наследование свойств в цепочке прототипов.</td>
+ </tr>
+ <tr>
+ <td>Определение класса определяет <em>все</em> свойства всех экземпляров класса. Нельзя динамически добавлять свойства во время выполнения.</td>
+ <td>Функция-конструктор или прототип задает <em>начальный</em> набор свойств. Можно добавить или удалить свойства динамически к отдельным объектам или всей совокупности объектов.</td>
+ </tr>
+ </tbody>
+</table>
+
+<h2 id="Пример_Сотрудник">Пример Сотрудник</h2>
+
+<p>Оставшаяся часть этой главы объясняет иерархию сотрудников, показанную на следующем рисунке:</p>
+
+<p><img alt="" class="internal" src="/@api/deki/files/4452/=figure8.1.png" style="height: 194px; width: 281px;"></p>
+
+<p><small><strong>Рисунок 8.1: Простая иерархия объектов</strong></small></p>
+
+<p>Этот пример использует следующие объекты:</p>
+
+<ul>
+ <li><code>Employee</code> имеет свойство <code>name</code> (значение которого по умолчанию пустая строка) и <code>dept</code> (значение которого по умолчанию "general").</li>
+ <li><code>Manager</code> основывается на <code>Employee</code>. Он добавляет свойство <code>reports</code> (значение которого по умолчанию пустой массив, предназначенный для хранения массива объектов <code>Employee</code>).</li>
+ <li><code>WorkerBee</code> так же основан на <code>Employee</code>. Он добавляет свойство <code>projects</code> (значение которого по умолчанию пустой массив, предназначенный для хранения строк).</li>
+ <li><code>SalesPerson</code> основан на <code>WorkerBee</code>. Он добавляет свойство <code>quota </code>(значение которого по умолчанию 100). Он также переопределяет свойство <code>dept</code>, со значением "sales", указывая, что все продавцы находятся в одном отделе.</li>
+ <li><code>Engineer</code> основан на <code>WorkerBee</code>. Он добавляет свойство <code>machine</code> (значение которого по умолчанию пустая строка), а так же определяет свойство <code>dept</code> значением "engineering".</li>
+</ul>
+
+<h2 id="Создание_иерархии">Создание иерархии</h2>
+
+<p>Известно несколько способов определить подходящие функции-конструкторы, которые реализуют иерархию <code>Employee</code>. Выбор способа определения в большей степени зависит от того, на что рассчитано ваше приложение.</p>
+
+<p>В этом разделе приведены очень простые (и сравнительно не гибкие) определения, для демонстрации того, как же работает наследование. В этих определениях, вы не можете указать значения свойствам при создании объекта. Свойства вновь созданного объекта попросту получают значения по умолчанию, которые можно изменить позднее.</p>
+
+<p>В реальном приложении, вы, вероятно, будете определять конструкторы, которые позволяют устанавливать нужные вам значения свойств во время создания объекта (см <a href="#Более_гибкие_конструкторы">Более гибкие конструкторы</a>). В данном же случае конструкторы упрощены сознательно для того, чтобы сфокусироваться на сути наследования.</p>
+
+<p>Следующие определения <code>Employee</code> для языков Java и JavaScript довольно похожи. Единственное отличие состоит в том, что вам необходимо указать тип каждого свойства в Java, но не в JavaScript (потому что Java является <a href="https://ru.wikipedia.org/wiki/Сильная_и_слабая_типизация">строго типизированным языком</a>, в то время как JavaScript слабо типизированный).</p>
+
+<table class="standard-table">
+ <thead>
+ <tr>
+ <th scope="col">JavaScript</th>
+ <th scope="col">Java</th>
+ </tr>
+ </thead>
+ <tbody>
+ <tr>
+ <td>
+ <pre class="brush: js">
+function Employee() {
+ this.name = '';
+ this.dept = 'general';
+}
+</pre>
+ </td>
+ <td>
+ <pre class="brush: java">
+public class Employee {
+ public String name = "";
+ public String dept = "general";
+}
+</pre>
+ </td>
+ </tr>
+ </tbody>
+</table>
+
+<p>Определения классов <code>Manager</code> и <code>WorkerBee</code> показывают разницу в определении вышестоящего объекта в цепочке наследования. В JavaScript вводится связующий объект (прототипный экземпляр), который присваивается в качестве значения свойству <code>prototype </code>функции-конструктора. Вы можете сделать это в любое время после того, как вы создали конструктор. В Java, необходимо указать суперкласс внутри определения класса. Вы не можете изменить суперкласс вне определения класса.</p>
+
+<table class="standard-table">
+ <thead>
+ <tr>
+ <th scope="col">JavaScript</th>
+ <th scope="col">Java</th>
+ </tr>
+ </thead>
+ <tbody>
+ <tr>
+ <td>
+ <pre class="brush: js">
+function Manager() {
+ Employee.call(this);
+ this.reports = [];
+}
+//создаем пустой бъект с прототипом от коструктора Employee
+//и используем этот объект как прототип для Manager
+Manager.prototype = Object.create(Employee.prototype);
+
+Manager.prototype.constructor = Manager;
+
+function WorkerBee() {
+ Employee.call(this);
+ this.projects = [];
+}
+WorkerBee.prototype = Object.create(Employee.prototype);
+WorkerBee.prototype.constructor = WorkerBee;
+</pre>
+ </td>
+ <td>
+ <pre class="brush: java">
+public class Manager extends Employee {
+ public Employee[] reports = new Employee[0];
+}
+
+public class WorkerBee extends Employee {
+ public String[] projects = new String[0];
+}
+</pre>
+ </td>
+ </tr>
+ </tbody>
+</table>
+
+<p>Классы <code>Engineer</code> и <code>SalesPerson</code> создают объекты, которые происходят от <code>WorkerBee</code> и, следовательно, от <code>Employee</code>. Объект этих типов имеет свойства всех объектов, расположенных над ним в иерархии. Также, эти классы переопределяют наследуемое значение свойства <code>dept</code> своими значениями, характерными для этих объектов.</p>
+
+<table class="standard-table">
+ <thead>
+ <tr>
+ <th scope="col">JavaScript</th>
+ <th scope="col">Java</th>
+ </tr>
+ </thead>
+ <tbody>
+ <tr>
+ <td>
+ <pre class="brush: js">
+function SalesPerson() {
+ WorkerBee.call(this);
+ this.dept = 'sales';
+ this.quota = 100;
+}
+SalesPerson.prototype = Object.create(WorkerBee.prototype);
+SalesPerson.prototype.constructor = SalesPerson;
+
+function Engineer() {
+ WorkerBee.call(this);
+ this.dept = 'engineering';
+ this.machine = '';
+}
+Engineer.prototype = Object.create(WorkerBee.prototype);
+Engineer.prototype.constructor = Engineer;
+</pre>
+ </td>
+ <td>
+ <pre class="brush: java">
+public class SalesPerson extends WorkerBee {
+ public double quota;
+ public dept = "sales";
+ public quota = 100.0;
+}
+
+public class Engineer extends WorkerBee {
+ public String machine;
+ public dept = "engineering";
+ public machine = "";
+}
+</pre>
+ </td>
+ </tr>
+ <tr>
+ <td></td>
+ <td></td>
+ </tr>
+ </tbody>
+</table>
+
+<p>Используя эти определения, вы можете создавать экземпляры объектов, которые получат значения по умолчанию для своих свойств. Рисунок 8.3 иллюстрирует использование этих определений и показывает значения свойств у полученных объектов.</p>
+
+<p>{{ note('Термин <em>экземпляр </em>имеет специфическое значение в языках, основанных на классах. В этих языках экземпляр — это индивидуальная сущность определенного класса и принципиально отличается от класса. В JavaScript «экземпляр» не имеет такого технического значения, потому что JavaScript не делает таких отличий между классами и экземплярами. Однако, в разговоре о JavaScript, термин «экземпляр» может неформально использоваться для обозначения объекта, созданного с использованием конкретной <span>функции конструктора. Так, в этом примере, вы можете неформально сказать, что <code>jane</code> является экземпляром <code>Engineer</code>. Аналогично, хотя термины <em>parent, child, ancestor</em> и <em>descendant</em> (<em>родитель, ребенок, предок</em> и <em>потомок</em>) не имеют формальных значений в JavaScript, вы можете использовать их неформально для ссылки на объекты выше или ниже</span> <span>в</span> <span>цепочке прототипов.') }}</span></p>
+
+<p><img alt="figure8.3.png" class="default internal" id="figure8.3" src="/@api/deki/files/4403/=figure8.3.png"><br>
+ <a id="8.3" name="8.3"><small><strong>Рисунок 8.3: Создание объектов с простыми определениями</strong></small></a></p>
+
+<h2 id="Свойства_объекта">Свойства объекта</h2>
+
+<p>Этот раздел о том, как объекты наследуют свойства из других объектов в цепочке прототипов, и что происходит, когда вы добавляете свойство во время выполнения.</p>
+
+<h3 id="Наследование_свойств">Наследование свойств</h3>
+
+<p>Предположим, вы создаете объект <code>mark</code> в качестве <code>WorkerBee</code> (как показано на <a href="#8.3">Рисунок 8.3</a>) с помощью следующего выражения:</p>
+
+<pre class="brush: js">var mark = new WorkerBee;
+</pre>
+
+<p>Когда JavaScript видит оператор <code>new</code>, он создает новый обобщенный объект и неявно устанавливает значение внутреннего свойства [[Prototype]] в <code>WorkerkBee.prototype</code>, затем передает этот новый объект в качестве значения <code>this</code> в функцию-конструктор <code>WorkerBee</code>. Внутреннее свойство [[Prototype]] определяет цепочку прототипов, используемых для получения значений свойств. После того, как эти свойства установлены, JavaScript возвращает новый объект, а оператор присваивания устанавливает переменную <code>mark</code> для этого объекта.</p>
+
+<p>Этот процесс не задает значения свойств (<em>локальных</em> значений), которые унаследованы по цепочке прототипов, объекта <code>mark</code> напрямую. Когда вы запрашиваете значение свойства, JavaScript сначала проверяет, существует ли это значение в данном объекте. Если так и есть, тогда возвращается это значение. Если значение не найдено в самом объекте, JavaScript проверяет цепочку прототипов (используя внутреннее свойство [[Prorotype]]). Если объект в цепочке прототипов имеет значение для искомого свойства, это значение возвращается. Если такое свойство не найдено, JavaScript сообщает, что объект не обладает свойством. Таким образом, объект <code>mark</code> содержит следующие свойства и значения:</p>
+
+<pre class="brush: js">mark.name = '';
+mark.dept = 'general';
+mark.projects = [];
+</pre>
+
+<p>Значения для свойств <code>name</code> и <code>dept</code> объекту <code>mark</code> присваиваются из конструктора <code>Employee</code>. Также из конструктора <code>WorkerBee</code> присваивается локальное значение для свойства <code>projects</code>. Это дает вам наследование свойств и их значений в JavaScript. Некоторые детали этого процесса обсуждаются в <a href="#Тонкости_наследования_свойств">Тонкости наследования свойств</a>.</p>
+
+<p>Поскольку эти конструкторы не позволяют вводить значения, специфичные для экземпляра, добавленная информация является общей. Значения свойств устанавливаются по умолчанию одинаковыми для всех объектов, созданных функцией <code>WorkerBee</code>. Конечно, вы можете изменить значения любого из этих свойств. Так, вы можете добавить специфичную информацию для <code>mark</code> следующим образом:</p>
+
+<pre class="brush: js">mark.name = 'Doe, Mark';
+mark.dept = 'admin';
+mark.projects = ['navigator'];</pre>
+
+<h3 id="Добавление_свойств">Добавление свойств</h3>
+
+<p>В JavaScript вы можете добавить свойства для любого объекта в реальном времени. Вы не ограничены только свойствами, установленными функцией-конструктором. Чтобы добавить свойство, специфичное для конкретного объекта, вы присваиваете ему значение в объекте, вот так:</p>
+
+<pre class="brush: js">mark.bonus = 3000;
+</pre>
+
+<p>Теперь объект <code>mark</code> имеет свойство <code>bonus</code>, но никакой другой <code>WorkerBee</code> не имеет этого свойства.</p>
+
+<p>Если вы добавляете новое свойство в объект, который используется в качестве прототипа для функции-конструктора, вы добавляете это свойство для всех объектов, наследующих свойства из этого прототипа. Например, вы можете добавить свойство <code>specialty</code> для всех сотрудников с помощью следующего выражения:</p>
+
+<pre class="brush: js">Employee.prototype.specialty = 'none';
+</pre>
+
+<p>Как только JavaScript выполняет это выражение, объект <code>mark</code> также получает свойство <code>specialty</code> со значением <code>"none"</code>. Следующий рисунок показывает результат добавления этого свойства в прототип <code>Employee</code> и последующее переопределение его в прототипе <code>Engineer</code>.</p>
+
+<p><img alt="" class="internal" src="/@api/deki/files/4422/=figure8.4.png" style="height: 519px; width: 833px;"><br>
+ <small><strong>Рисунок 8.4: Добавление свойств</strong></small></p>
+
+<h2 id="Более_гибкие_конструкторы">Более гибкие конструкторы</h2>
+
+<p><span id="result_box" lang="ru"><span>Функции</span>-<span>конструкторы</span><span>, показанные</span> <span>до сих пор,</span> <span>не</span> <span>позволяют задавать</span> <span>значения</span> <span>свойств при создании</span> <span>экземпляра. Как и в</span> <span>Java</span><span>,</span> <span>вы можете передать</span> <span>аргументы в</span> <span>конструкторах</span> <span>для инициализации значений свойств</span><span> экземпляров</span><span>.</span> <span>На следующем рисунке показан</span> <span>один из способов сделать</span> <span>это</span><span>.</span></span></p>
+
+<p><img alt="" class="internal" id="figure8.5" src="/@api/deki/files/4423/=figure8.5.png" style="height: 481px; width: 1012px;"><br>
+ <a id="8.5" name="8.5"><small><strong>Рисунок 8.5: Определение свойств в конструкторе, вариант 1</strong></small></a></p>
+
+<p>Следующая таблица показывает определения для этих объектов в JavaScript и Java.</p>
+
+<table class="standard-table">
+ <thead>
+ <tr>
+ <th scope="col">JavaScript</th>
+ <th scope="col">Java</th>
+ </tr>
+ </thead>
+ <tbody>
+ <tr>
+ <td>
+ <pre class="brush: js">
+function Employee (name, dept) {
+ this.name = name || '';
+ this.dept = dept || 'general';
+}
+</pre>
+ </td>
+ <td>
+ <pre class="brush: java">
+public class Employee {
+ public String name;
+ public String dept;
+ public Employee () {
+ this("", "general");
+ }
+ public Employee (String name) {
+ this(name, "general");
+ }
+ public Employee (String name, String dept) {
+ this.name = name;
+ this.dept = dept;
+ }
+}
+</pre>
+ </td>
+ </tr>
+ <tr>
+ <td>
+ <pre class="brush: js">
+function WorkerBee (projs) {
+
+ this.projects = projs || [];
+}
+WorkerBee.prototype = new Employee;
+</pre>
+ </td>
+ <td>
+ <pre class="brush: java">
+public class WorkerBee extends Employee {
+ public String[] projects;
+ public WorkerBee () {
+ this(new String[0]);
+ }
+ public WorkerBee (String[] projs) {
+ projects = projs;
+ }
+}
+
+</pre>
+ </td>
+ </tr>
+ <tr>
+ <td>
+ <pre class="brush: js">
+
+function Engineer (mach) {
+ this.dept = 'engineering';
+ this.machine = mach || '';
+}
+Engineer.prototype = new WorkerBee;
+</pre>
+ </td>
+ <td>
+ <pre class="brush: java">
+public class Engineer extends WorkerBee {
+ public String machine;
+ public Engineer () {
+ dept = "engineering";
+ machine = "";
+ }
+ public Engineer (String mach) {
+ dept = "engineering";
+ machine = mach;
+ }
+}
+</pre>
+ </td>
+ </tr>
+ </tbody>
+</table>
+
+<p>В JavaScript эти определения используют специальную идеому для установки значений по умолчанию:</p>
+
+<pre class="brush: js">this.name = name || '';
+</pre>
+
+<p>В JavaScript логический оператор ИЛИ (<code>||</code>) оценивает свой первый аргумент. Если этот аргумент преобразуется в true, оператор возвращает его. Иначе, оператор возвращает значение второго аргумента. Следовательно, эта строчка кода проверяет, содержит ли аргумент <code>name</code> значение, пригодное для свойства <code>name</code>. Если так и есть, <code>this.name</code> определяется этим значением. В противном случае, значению <code>this.name</code> присваивается пустая строка. Эта глава использует такую идиому для краткости; тем не менее, с первого взгляда она может озадачить.</p>
+
+<p>{{ note('Это может работать не так, как ожидается, если функция-конструктор вызывается с аргументами, которые преобразуются в <code><code>false</code></code>, вроде нуля (<code>0</code>) или пустой строки (<code>""</code>). В этом случае будет выбрано значение по умолчанию.') }}</p>
+
+<p>С помощью таких определений, создавая экземпляр объекта, вы можете указать значения для локально определенных свойств. Как показано на <a href="#8.5">Рисунок 8.5</a>, можно использовать следующее выражение для создания нового <code>Engineer</code>:</p>
+
+<pre class="brush: js">var jane = new Engineer('belau');
+</pre>
+
+<p>Свойства созданного объекта <code>jane</code>:</p>
+
+<pre class="brush: js">jane.name == '';
+jane.dept == 'engineering';
+jane.projects == [];
+jane.machine == 'belau'
+</pre>
+
+<p>Обратите внимание, что с таким способом вы не можете указать начальное значение наследуемого свойства, такого как <code>name</code>. Если вы хотите задать начальное значение для наследуемых свойств в JavaScript, вам нужно добавить больше кода в функцию-конструктор.</p>
+
+<p>До сих пор функция-конструктор создавала обобщенный объект, а затем определяла локальные свойства и значения для нового объекта. Вы можете использовать конструктор, который добавляет дополнительные свойства путем непосредственного вызова функции-конструктора для объекта, расположенного выше в цепочке прототипов. На следующем рисунке показаны эти новые определения.</p>
+
+<p><img alt="" class="internal" src="/@api/deki/files/4430/=figure8.6.png" style="height: 534px; width: 1063px;"><br>
+ <small><strong>Рисунок 8.6: Определение свойств в конструкторе, вариант 2</strong></small></p>
+
+<p>Давайте рассмотрим одно из этих определений детальнее. Вот новое определение функции-конструктора <code>Engineer</code>:</p>
+
+<pre class="brush: js">function Engineer (name, projs, mach) {
+ this.base = WorkerBee;
+ this.base(name, 'engineering', projs);
+ this.machine = mach || '';
+}
+</pre>
+
+<p>Предположим, вы создаете новый объект, используя <code>Engineer, следующим образом:</code></p>
+
+<pre class="brush: js">var jane = new Engineer('Doe, Jane', ['navigator', 'javascript'], 'belau');
+</pre>
+
+<p>JavaScript <span id="result_box" lang="ru"><span>выполняет следующие действия</span><span>:</span></span></p>
+
+<ol>
+ <li>Оператор <code>new</code> создает обобщенный объект и устанавливает его свойству <code>__proto__</code> значение <code>Engineer.prototype</code>.</li>
+ <li>Оператор <code>new</code> передает этот новый объект в конструктор <code>Engineer</code> в качестве значения ключевого слова <code>this</code>.</li>
+ <li>Конструктор создает новое свойство с именем <code>base</code> для этого объекта и присваивает значение свойства <code>base</code> из конструктора <code>WorkerBee</code>. Это делает конструктор <code>WorkerBee</code> методом объекта, созданного <code>Engineer</code>. Имя свойства <code>base</code> не является специальным словом. Вы можете использовать любое допустимое для свойства имя; <code>base</code> всего-лишь напоминает о предназначении свойства.</li>
+ <li>Конструктор вызывает метод <code>base</code>, передавая в качестве аргументов два аргумента, переданных конструктору (<code>"Doe, Jane"</code> и <code>["navigator", "javascript"]</code>), а также строку <code>"engineering"</code>. Явное использование <code>"engineering"</code> в конструкторе указывает на то, что все объекты, созданные <code>Engineer</code>, имеют одинаковое значение для наследуемого свойства <code>dept</code>, это значение переопределяет значение, унаследованное из <code>Employee</code>.</li>
+ <li>Поскольку <code>base</code> является методом <code>Engineer</code>, внутри вызова <code>base</code> JavaScript привязывает ключевое свойство <code>this</code> к объекту, созданному в шаге 1. Таким образом, функция <code>WorkerBee</code> передает поочередно аргументы <code>"Doe, Jane"</code> и <code>"engineering"</code> в функцию-конструктор <code>Employee</code>. Получив результат из <code>Employee</code>, функция <code>WorkerBee</code> использует оставшийся аргумент для установки значения свойства <code>projects</code>.</li>
+ <li>После возвращения из метода <code>base</code>, конструктор <code>Engineer</code> инициализирует свойство объекта <code>machine</code> со значением <code>"belau"</code>.</li>
+ <li>После возвращения из конструктора, JavaScript присваивает новый объект переменной <code>jane</code>.</li>
+</ol>
+
+<p>Можно подумать, что вызвав <code>WorkerBee</code> из конструктора <code>Engineer</code>, вы настроили соответствующим образом наследование для объектов, создаваемых <code>Engineer</code>. Это не так. Вызов конструктора <code>WorkerBee</code> обеспечивает только то, что объект <code>Engineer</code> запускается со  свойствами, определенными во всех функциях-конструкторах, которые были вызваны. Так, если позже добавить свойства в прототипы <code>Employee</code> или <code>WorkerBee</code>, эти свойства не унаследуются объектами из <code>Engineer</code>. Например, предположим, вы использовали следующие определения:</p>
+
+<pre class="brush: js">function Engineer (name, projs, mach) {
+ this.base = WorkerBee;
+ this.base(name, 'engineering', projs);
+ this.machine = mach || '';
+}
+var jane = new Engineer('Doe, Jane', ['navigator', 'javascript'], 'belau');
+Employee.prototype.specialty = 'none';
+</pre>
+
+<p>В примере выше <code>jane</code> не унаследует свойство <code>specialty</code>. Для динамического наследования необходимо явно устанавливать прототип, что и сделано в следующем дополнении:</p>
+
+<pre class="brush: js">function Engineer (name, projs, mach) {
+ this.base = WorkerBee;
+ this.base(name, 'engineering', projs);
+ this.machine = mach || '';
+}
+Engineer.prototype = new WorkerBee;
+var jane = new Engineer('Doe, Jane', ['navigator', 'javascript'], 'belau');
+Employee.prototype.specialty = "none";
+</pre>
+
+<p>Теперь свойство <code>specialty</code> объекта <code>jane</code> имеет значение "none".</p>
+
+<p>Другой способ вызвать родительский конструктор в контексте создаваемого объекта это использование методов <a href="/en-US/docs/JavaScript/Reference/Global_Objects/Function/call" title="en-US/docs/JavaScript/Reference/Global Objects/Function/call"><code>call()</code></a> / <a href="/en-US/docs/JavaScript/Reference/Global_Objects/Function/apply" title="en-US/docs/JavaScript/Reference/Global Objects/Function/apply"><code>apply()</code></a>. Следующие блоки эквивалентны:</p>
+
+<table>
+ <tbody>
+ <tr>
+ <td>
+ <pre class="brush: js">
+function Engineer (name, projs, mach) {
+ this.base = WorkerBee;
+ this.base(name, 'engineering', projs);
+ this.machine = mach || '';
+}
+</pre>
+ </td>
+ <td>
+ <pre class="brush: js">
+function Engineer (name, projs, mach) {
+ WorkerBee.call(this, name, 'engineering', projs);
+ this.machine = mach || '';
+}
+</pre>
+ </td>
+ </tr>
+ </tbody>
+</table>
+
+<p>Использование метода <code>call()</code> является более чистой реализацией наследования, так как он не требует создания дополнителного свойства, именованного в примере как <code>base</code>.</p>
+
+<h2 id="Тонкости_наследования_свойств">Тонкости наследования свойств</h2>
+
+<p>В секции выше рассказывалось каким образом конструкторы и прототипы в JavaScript обеспечивают иерархию и наследование. В секции ниже будут затронуты тонкости, которые выше были не так очевидны.</p>
+
+<h3 id="Локальные_значения_против_унаследованных">Локальные значения против унаследованных</h3>
+
+<p>Когда вы пытаетесь получить значение некоторого свойства объекта, JavaScript выполняет следующие шаги, которые уже перечислялись ранее в этой главе:</p>
+
+<ol>
+ <li>Проверяется, существует ли локальное свойство с запрашиваемым именем. Если да, то возвращается значение этого свойства.</li>
+ <li>Если локального свойства не существует, проверяется цепочка прототипов (через использование свойства <code>__proto__</code>).</li>
+ <li>Если один из объектов в цепочке прототипов имеет свойство c запрашиваемым именем, возвращается значение этого свойства.</li>
+ <li>Если искомое свойство не обнаружено, считается, что объект его не имеет.</li>
+</ol>
+
+<p>Результат выполнения этих шагов будет зависеть от того, в каком порядке вы создаете объекты, прототипы и их свойства. Рассмотрим пример:</p>
+
+<pre class="brush: js">function Employee () {
+ this.name = "";
+ this.dept = "general";
+}
+
+function WorkerBee () {
+ this.projects = [];
+}
+WorkerBee.prototype = new Employee;
+</pre>
+
+<p>Предположим, на основе конструкции выше, вы создаете объект <code>amy</code> как экземпляр класса <code>WorkerBee</code> следующим выражением:</p>
+
+<pre class="brush: js">var amy = new WorkerBee;
+</pre>
+
+<p><code><font face="Open Sans, Arial, sans-serif">В результате, объект </font>amy</code> будет иметь одно локальное своство - <code>projects</code>. Свойства <code>name</code> и <code>dept</code> не будут локальными для <code>amy</code> но будут взяты из прототипа (объект на который ссылается свойство <code>__proto__</code> объекта <code>amy)</code>. Таким образом, <code>amy</code> имеет три свойства:</p>
+
+<pre class="brush: js">amy.name == "";
+amy.dept == "general";
+amy.projects == [];
+</pre>
+
+<p>Теперь предположим, что вы изменили значение свойства <code>name</code> у прототипа <code>Employee</code>:</p>
+
+<pre class="brush: js">Employee.prototype.name = "Unknown"
+</pre>
+
+<p>На первый взгляд вы можете ожидать, что это изменение распространится на все экземпляры <code>Employee</code>. Однако этого не случится.</p>
+
+<p>Когда вы устанавливаете прототип для <code>WorkerBee</code> вы создаете новый объект <code>Employee</code>, таким образом <code>WorkerBee.prototype</code> получает свое собственное локальное свойство <code>name</code> (в данном примере пустую строку). Следовательно, когда JavaScript ищет свойство <code>name</code> у объекта <code>amy</code> (экземпляра <code>WorkerBee</code>), он первым делом натыкается на него в прототипе <code>WorkerBee.prototype,</code> и до проверки <code>Employee.prototype</code> дело не доходит.</p>
+
+<p>Если у вас есть необходимость изменять некоторое свойство объекта во время работы приложения, и применять это изменение на все существующие экземпляры, не нужно создавать это свойство внутри конструктора. Вместо этого добавьте свойство в прототип, принадлежащий конструктору. Для примера, предположим, вы изменили код, который был показан выше, следующим образом:</p>
+
+<pre class="brush: js">function Employee () {
+ this.dept = "general";
+}
+Employee.prototype.name = "";
+
+function WorkerBee () {
+ this.projects = [];
+}
+WorkerBee.prototype = new Employee;
+
+var amy = new WorkerBee;
+
+Employee.prototype.name = "Unknown";
+</pre>
+
+<p>в таком случае свойство <code>name</code> у объекта <code>amy</code> примет значение "Unknown".</p>
+
+<p>Как показано в этом примере, если вы хотите иметь значения свойств по умолчанию, и иметь возможность менять эти значения во время работы приложения, создавайте их в прототипе конструктора, а не в самом конструкторе.</p>
+
+<h3 id="Разбираемся_во_взаимосвязи_экземпляров">Разбираемся во взаимосвязи экземпляров</h3>
+
+<p>Поиск свойств в JavaScript начинается с просмотра самого объекта, и если в нем свойство не найдено, поиск переключается на объект, на который указывает ссылка <code>__proto__</code>. Это продолжается рекурсивно и такой процесс поиска называется "поиск в цепочке прототипов".</p>
+
+<p>Специальное свойство <code>__proto__</code> устанавливается автоматически при создании объекта. Оно принимает значение свойства <code>prototype</code> функции-конструктора. Таким образом, <code>new Foo()</code> создаст объект для которого справедливо выражение <code>__proto__ == <code class="moz-txt-verticalline">Foo.prototype</code></code>. Вследствие этого, любые изменения свойств у <code class="moz-txt-verticalline">Foo.prototype,</code> оказывают эффект на процесс поиска свойств во всех объектах, созданных при помощи <code>new Foo()</code>.</p>
+
+<p>Все объекты (за исключением глобального объекта <code>Object</code>) имеют свойство <code>__proto__.</code> Все функции имеют свойство <code>prototype</code>. Благодаря этому, могут быть установлены родственные связи в иерархии объектов. Вы можете установить родство и происхождение объекта, сравнив его свойство <code>__proto__</code> со свойством <code>prototype</code> конструктора. Здесь JavaScript представляет оператор <code>instanceof</code> как более простой способ проверки, наследуется ли объект от конкретного конструктора. Для примера:</p>
+
+<pre class="brush: js">var f = new Foo();
+var isTrue = (f instanceof Foo);</pre>
+
+<p>Для более детального примера, предположим, у вас имеются те же определения, что приведены в разделе <a href="#Inheriting_properties">Inheriting properties</a>. Создадим экземпляр <code>Engineer</code> как показано здесь:</p>
+
+<pre class="brush: js">var chris = new Engineer("Pigman, Chris", ["jsd"], "fiji");
+</pre>
+
+<p>Для полученного объекта будут истинными все из следующих выражений:</p>
+
+<pre class="brush: js">chris.__proto__ == Engineer.prototype;
+chris.__proto__.__proto__ == WorkerBee.prototype;
+chris.__proto__.__proto__.__proto__ == Employee.prototype;
+chris.__proto__.__proto__.__proto__.__proto__ == Object.prototype;
+chris.__proto__.__proto__.__proto__.__proto__.__proto__ == null;
+</pre>
+
+<p>Зная это, вы можете написать свою функцию <code>instanceOf</code> как показано ниже:</p>
+
+<pre class="brush: js">function instanceOf(object, constructor) {
+ object = object.__proto__;
+ while (object != null) {
+ if (object == constructor.prototype)
+ return true;
+ if (typeof object == 'xml') {
+ return constructor.prototype == XML.prototype;
+ }
+ object = object.__proto__;
+ }
+ return false;
+}
+</pre>
+
+<div class="note"><strong>Замечание:</strong> Реализация выше особым образом обрабатывает тип "xml". Это сделано для того, чтобы обойти особенность представления XML объектов в последних версиях JavaScript. Смотрите описание ошибки {{ bug(634150) }} если вам интересны детали.</div>
+
+<p>Следующие вызовы функции instanceOf, заданной выше, вернут истинные значения:</p>
+
+<pre>instanceOf (chris, Engineer)
+instanceOf (chris, WorkerBee)
+instanceOf (chris, Employee)
+instanceOf (chris, Object)
+</pre>
+
+<p>Но следующее выражение вернет <code>false</code>:</p>
+
+<pre class="brush: js">instanceOf (chris, SalesPerson)
+</pre>
+
+<h3 id="Глобальные_данные_в_конструкторах">Глобальные данные в конструкторах</h3>
+
+<p>При написании конструкторов, следует с особым вниманием относиться к изменению глобальных переменных. Например, если вам нужен уникальный ID, который был бы автоматически назначен каждому экземпляру <code>Employee,</code> вы примените следующий подход для определения <code>Employee</code>:</p>
+
+<pre class="brush: js">var idCounter = 1;
+
+function Employee (name, dept) {
+ this.name = name || "";
+ this.dept = dept || "general";
+ this.id = idCounter++;
+}
+</pre>
+
+<p>Здесь, когда вы создаете новый экземпляр <code>Employee</code>, конструктор присваивает ему все новый и новый ID увеличивая значение глобальной переменной <code>idCounter</code>. Следовательно, при выполнении кода ниже, <code>victoria.id</code> станет равным 1 а <code>harry.id</code> — 2:</p>
+
+<pre class="brush: js">var victoria = new Employee("Pigbert, Victoria", "pubs")
+var harry = new Employee("Tschopik, Harry", "sales")
+</pre>
+
+<p>Навскидку, все выглядит предсказуемо. Однако, <code>idCounter</code> увеличивается при создании каждого объекта <code>Employee</code> вне зависимости от цели его создания. Если вы создаете полную иерархию класса <code>Employee,</code> показанную выше в этой главе, конструктор <code>Employee</code> будет так же вызван каждый раз, когда вы устанавливаете прототип для подклассов. Следующий код раскрывает суть возможной проблемы:</p>
+
+<pre class="brush: js">var idCounter = 1;
+
+function Employee (name, dept) {
+ this.name = name || "";
+ this.dept = dept || "general";
+ this.id = idCounter++;
+}
+
+function Manager (name, dept, reports) {...}
+Manager.prototype = new Employee;
+
+function WorkerBee (name, dept, projs) {...}
+WorkerBee.prototype = new Employee;
+
+function Engineer (name, projs, mach) {...}
+Engineer.prototype = new WorkerBee;
+
+function SalesPerson (name, projs, quota) {...}
+SalesPerson.prototype = new WorkerBee;
+
+var mac = new Engineer("Wood, Mac");
+</pre>
+
+<p>Предположим, каждый из конструкторов, тело которого опущено для краткости, содержит вызов конструктора прародителя. Это приведет к тому, что <code>id</code> у объекта <code>mac</code> примет значение 5 вместо ожидаемой единицы.</p>
+
+<p>В зависимости от приложения, лишние увеличения счетчика могут быть не критичны. В случае же, когда точный контроль за значениями счетчика важен, одним из возможных решений станет такой код:</p>
+
+<pre class="brush: js">function Employee (name, dept) {
+ this.name = name || "";
+ this.dept = dept || "general";
+ if (name)
+ this.id = idCounter++;
+}
+</pre>
+
+<p>Когда вы создаете экземпляр <code>Employee</code> в качестве прототипа, вы не предоставляете аргументы в конструктор за ненадобностью. Конструктор выше проверяет наличие аргумента <code>name,</code> и в случае, если значение не указано, идентификатор id объекту не присваивается, а значение глобального счетчика <code>idCounter</code> не увеличивается. Таким образом, для получения уникального <code>id</code> становится обязательным указание параметра <code>name</code> при вызове конструктора <code>Employee</code>. С внесенными в пример выше изменениями, <code>mac.id</code> станет равным долгожданной, заветной единице.</p>
+
+<h3 id="Никакого_множественного_наследования">Никакого множественного наследования</h3>
+
+<p>Некоторые из объектно-ориентированных языков предоставляют возможность множественного наследования. Когда один объект может унаследовать свойства и методы множества других, не связанных друг с другом объектов. В JavaScript такого не предусмотрено.</p>
+
+<p>В JavaScript наследование свойств осуществляется путем поиска в цепочке прототипов. Так как объект может иметь лишь единственный присвоенный ему прототип, JavaScript не может осуществить наследование более чем от одной цепочки прототипов.</p>
+
+<p>Однако конструктор в JavaScript может вызывать любое количество вложенных конструкторов. Это дает некоторую, хоть и ограниченную (отсутствием прототипной связанности) видимость множественного наследования. Рассмотрим следуюший фрагмент:</p>
+
+<pre class="brush: js">function Hobbyist (hobby) {
+ this.hobby = hobby || "scuba";
+}
+
+function Engineer (name, projs, mach, hobby) {
+ this.base1 = WorkerBee;
+ this.base1(name, "engineering", projs);
+ this.base2 = Hobbyist;
+ this.base2(hobby);
+ this.machine = mach || "";
+}
+Engineer.prototype = new WorkerBee;
+
+var dennis = new Engineer("Doe, Dennis", ["collabra"], "hugo")
+</pre>
+
+<p>Предполагается, что определение <code>WorkerBee</code> задано ранее, как уже было показано в этой главе. В таком случае список свойств для объекта <code>dennis</code> примет следующий вид:</p>
+
+<pre class="brush: js">dennis.name == "Doe, Dennis"
+dennis.dept == "engineering"
+dennis.projects == ["collabra"]
+dennis.machine == "hugo"
+dennis.hobby == "scuba"
+</pre>
+
+<p>Мы видим, что <code>dennis</code> получил свойство <code>hobby</code> из конструктора <code>Hobbyist</code>. Однако, если вы добавите любое свойство в прототип конструктора <code>Hobbyist</code>:</p>
+
+<pre class="brush: js">Hobbyist.prototype.equipment = ["mask", "fins", "regulator", "bcd"]
+</pre>
+
+<p>Объект <code>dennis</code> этого свойства не унаследует.</p>
+
+<div>{{PreviousNext("Web/JavaScript/Guide/Working_with_Objects", "Web/JavaScript/Guide/Iterators_and_Generators")}}</div>