Наконец, обратите также внимание на одинаковые фрагменты примеров 9.1 и 9.2: в обоих классах методы объекта range определяются и вызываются одинаковым способом.
9.2.1. Конструкторы и идентификация класса
Как видите, объект-прототип играет чрезвычайно важную роль в идентификации класса: два объекта являются экземплярами одного класса, только если они наследуют один и тот же объект-прототип. Функция-конструктор, инициализирующая свойства нового объекта, не является определяющей: два конструктора могут иметь свойства prototype , ссылающиеся на один объект-прототип. В этом случае оба конструктора будут создавать экземпляры одного и того же класса.
Хотя конструкторы не играют такую же важную роль в идентификации класса, как прототипы, тем не менее конструкторы выступают в качестве фасада класса. Например, имя конструктора обычно используется в качестве имени класса. Так, принято говорить, что конструктор Range() создает объекты класса Range . Однако более важным применением конструкторов является их использование в операторе instanceof при проверке принадлежности объекта классу. Если имеется объект r, и необходимо проверить, является ли он объектом класса Range , такую проверку можно выполнить так:
r instanceof Range // вернет true, если r наследует Range.prototype
В действительности оператор instanceof не проверяет, был ли объект r инициализирован конструктором Range . Он проверяет, наследует ли этот объект свойство Range.prototype . Как бы то ни было, синтаксис оператора instanceof закрепляет использование конструкторов в качестве идентификаторов классов. Мы еще встретимся с оператором instanceof далее в этой главе.
9.2.2. Свойство constructor
В примере 9.2 свойству Range.prototype присваивался новый объект, содержащий методы класса. Хотя было удобно определить методы как свойства единственного объекта-литерала, но при этом совершенно не было необходимости создавать новый объект. Роль конструктора в языке JavaScript может играть любая функция, поскольку выражению вызова конструктора необходимо лишь свойство рrototype . Следовательно, любая функция (кроме функций, возвращаемых методом Function.bind() в ECMAScript 5) автоматически получает свойство prototype . Значением этого свойства является объект, который имеет единственное неперечислимое свойство constructor . Значением свойства constructor является объект функции:
var F = function() {}; // Это объект функции.
var р = F.prototype; // Это объект-прототип, связанный с ней.
var c = p.constructor; // Это функция, связанная с прототипом.
c === F // => true: F.prototype.constructor === F для всех функций
Наличие предопределенного объекта-прототипа со свойством constructor означает, что объекты обычно наследуют свойство constructor , которое ссылается на их конструкторы. Поскольку конструкторы играют роль идентификаторов классов, свойство constructor определяет класс объекта:
var о = new F(); // Создать объект класса F
о.constructor === F // => true: свойство constructor определяет класс
Эти взаимосвязи между функцией-конструктором, ее прототипом, обратной ссылкой из прототипа на конструктор и экземплярами, созданными с помощью конструктора, иллюстрируются на рис. 9.1.

Обратите внимание, что в качестве примера для рис. 9.1 был взят наш конструктор Range(). Однако в действительности класс Range , определенный в примере 9.2, замещает предопределенный объект Range.prototype своим собственным. А новый объект-прототип не имеет свойства constructor . По этой причине экземпляры класса Range , как следует из определения, не имеют свойства constructor . Решить эту проблему можно, явно добавив конструктор в прототип:
Range.prototype = {
constructor: Range, // Явно установить обратную ссылку на конструктор
includes: function(x) { return this.from <= x && x <= this.to; },
foreach: function(f) {
Читать дальше
Конец ознакомительного отрывка
Купить книгу