Далее отметьте, что конструктор Range() вызывается (в конце примера) с ключевым словом new, тогда как фабричная функция range() вызывается без него. В примере 9.1 для создания нового объекта использовался вызов обычной функции (раздел 8.2.1), а в примере 9.2 - вызов конструктора (раздел 8.2.3). Поскольку конструктор Range() вызывается с ключевым словом new, отпадает необходимость вызывать функцию inherit() или предпринимать какие-либо другие действия по созданию нового объекта. Новый объект создается автоматически перед вызовом конструктора и доступен в конструкторе как значение this. Конструктору Range() остается лишь инициализировать его. Конструкторы даже не должны возвращать вновь созданный объект. Выражение вызова конструктора автоматически создает новый объект, вызывает конструктор как метод этого объекта и возвращает объект. Тот факт, что вызов конструктора настолько отличается от вызова обычной функции, является еще одной причиной, почему конструкторам принято давать имена, начинающиеся с заглавного символа. Конструкторы предназначены для вызова в виде конструкторов, с ключевым словом new, и обычно при вызове в виде обычных функций они не способны корректно выполнять свою работу. Соглашение по именованию конструкторов, обеспечивающее визуальное отличие имен конструкторов от имен обычных функций, помогает программистам не забывать использовать ключевое слово new.
Еще одно важное отличие между примерами 9.1 и 9.2 заключается в способе именования объекта-прототипа. В первом примере прототипом было свойство range.methods. Это было удобное, описательное имя, но в значительной мере произвольное. Во втором примере прототипом является свойство Range.prototype, и это имя является обязательным. Выражение вызова конструктора Range() автоматически использует свойство Range.prototype как прототип нового объекта Range.
Наконец, обратите также внимание на одинаковые фрагменты примеров 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 является объект функции: