JavaScript. Подробное руководство, 6-е издание, стр. 109

<b>var р = F.prototype;   </b>
<b>// Это объект-прототип, связанный с ней.</b>

<b>var c = p.constructor; </b>
<b>// Это функция, связанная с прототипом.</b>

<b>c === F    </b>
<b>// =&gt; true: F.prototype.constructor === F для всех функций</b>

Наличие предопределенного объекта-прототипа со свойством

<b>constructor</b>
означает, что объекты обычно наследуют свойство
<b>constructor</b>
, которое ссылается на их конструкторы. Поскольку конструкторы играют роль идентификаторов классов, свойство
<b>constructor</b>
определяет класс объекта:

<b>var о = new F(); // Создать объект класса F</b>

<b>о.constructor === F // =&gt; true: свойство constructor определяет класс</b>

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

JavaScript. Подробное руководство, 6-е издание - i_011.jpg

Обратите внимание, что в качестве примера для рис. 9.1 был взят наш конструктор

<b>Range().</b>
Однако в действительности класс
<b>Range</b>
, определенный в примере 9.2, замещает предопределенный объект
<b>Range.prototype</b>
своим собственным. А новый объект-прототип не имеет свойства
<b>constructor</b>
. По этой причине экземпляры класса
<b>Range</b>
, как следует из определения, не имеют свойства
<b>constructor</b>
. Решить эту проблему можно, явно добавив конструктор в прототип:

<b>Range.prototype = {</b>

<b>  constructor: Range, // Явно установить обратную ссылку на конструктор </b>

<b>  includes: function(x) { return this.from &lt;= x &amp;&amp; x &lt;= this.to; }, </b>

<b>  foreach: function(f) {</b>

<b>    for(var x = Math.ceil(this.from); x &lt;= this.to; x++) f(x);</b>

<b>  },</b>

<b>  toString: function() { return &quot;(&quot; + this.from + “...&quot; + this.to + }</b>

<b>};</b>

Другой типичный способ заключается в том, чтобы использовать предопределенный объект-прототип, который уже имеет свойство constructor, и добавлять методы в него:

<b>// Здесь расширяется предопределенный объект Range.prototype,</b>

<b>// поэтому не требуется переопределять значение автоматически </b>

<b>// создаваемого свойства Range.prototype.constructor.</b>

<b>Range.prototype.includes = function(x) { return this.from&lt;=x &amp;&amp; x&lt;=this.to; };</b>

<b>Range.prototype.foreach = function(f) {</b>

<b>  for(var x = Math.ceil(this.from); x &lt;= this.to; x++) f(x);</b>

<b>};</b>

<b>Range.prototype.toString = function() {</b>

<b>  return &quot;(&quot; + this, from + &quot;...&quot; + this, to + &quot;)&quot;;</b>

<b>};</b>

9.3. Классы в стиле Java

Если вам приходилось программировать на языке Java или других объектно-ориентированных языках со строгим контролем типов, вы, возможно, привыкли считать, что классы могут иметь четыре типа членов:

Поля экземпляра

Это свойства, или переменные экземпляра, хранящие информацию о конкретном объекте.

Методы экземпляров

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

Поля класса

Это свойства, или переменные, всего класса в целом, а не конкретного экземпляра.

Методы класса

Методы всего класса в целом, а не конкретного экземпляра.

Одна из особенностей языка JavaScript, отличающая его от языка Java, состоит в том, что функции в JavaScript являются значениями, и поэтому нет четкой границы между методами и полями. Если значением свойства является функция, это свойство определяется как метод. В противном случае это обычное свойство, или «поле». Но, несмотря на эти отличия, имеется возможность имитировать все четыре категории членов классов в языке JavaScript. Определение любого класса в языке JavaScript вовлекает три различных объекта (рис. 9.1), а свойства этих трех объектов действуют подобно различным категориям членов класса:

Объект-конструктор

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

Объект-прототип

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

Объект экземпляра

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

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

<b>defineClass().</b>
(В ней используется функция
<b>extend()</b>
из примера 6.2 с исправлениями из примера 8.3):

<b>// Простая функция для определения простых классов</b>

<b>function defineClass(constructor, // Функция, определяющая свойства экземпляра </b>

<b>                     methods, // Методы экземпляров: копируются в прототип </b>