Шрифт:
Пример 9.2. Реализация класса Range с помощью конструктора
// range2.js: Еще один класс, представляющий диапазон значений.
// Это функция-конструктор, которая инициализирует новые объекты Range.
// Обратите внимание, что она не создает и не возвращает объект.
// Она лишь инициализирует его.
function Range(from, to) {
// Сохранить начальное и конечное значения в новом объекте range.
// Это не унаследованные свойства, и они являются уникальными для данного объекта,
this.from = from;
this.to = to;
}
// Все объекты Range наследуют свойства этого объекта.
// Обратите внимание, что свойство обязательно должно иметь имя "prototype".
Range.prototype = {
// Возвращает true, если х - объект класса range, в противном случае возвращает false
// Этот метод может работать не только с числовыми диапазонами, но также
// с текстовыми диапазонами и с диапазонами дат Date,
includes: function(x) { return this.from <= x && x <= this.to; },
// Вызывает f для каждого целого числа в диапазоне.
// Этот метод может работать только с числовыми диапазонами,
foreach: function(f) {
for(var х = Math.ceil(this.from); x <= this.to; x++) f(x);
},
// Возвращает строковое представление диапазона
toString: function { return "(" + this, from + "..." + this, to + ")"; }
};
// Ниже приводится пример использования объекта range.
var r = new Range(1.3); // Создать новый объект range
r.includes(2); // => true: число 2 входит в диапазон
r.foreach(console.log); // Выведет 1 2 3
console.log(r); // Выведет (1...3)
Имеет смысл детально сравнить примеры 9.1 и 9.2 и отметить различия между этими двумя способами определения классов. Во-первых, обратите внимание, что при преобразовании в конструктор фабричная функция
range
была переименована в Range.
Это обычное соглашение по оформлению: функции-конструкторы в некотором смысле определяют классы, а имена классов начинаются с заглавных символов. Имена обычных функций и методов начинаются со строчных символов. Далее отметьте, что конструктор
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
.