// и определяет общие методы (поведение) для всех объектов range,
var r = inherit(range.methods);
// Сохранить начальное и конечное значения в новом объекте range.
// Это не унаследованные свойства, и они являются уникальными для данного объекта,
r.from = from;
r.to = to;
// В заключение вернуть новый объект
return r;
}
// Ниже следует объект-прототип, определяющий методы, наследуемые всеми объектами range,
range.methods = {
// Возвращает 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 = range(1,3); // Создать новый объект range
r.includes(2); // => true: число 2 входит в диапазон
r.foreach(console.log);// Выведет 1 2 3
console.log(r); // Выведет (1...3)
В примере 9.1 есть несколько интересных моментов, которые следует отметить особо. Здесь определяется фабричная функция range(), которая используется для создания новых объектов range. Обратите внимание, что для хранения объекта-прототипа, определяющего класс, используется свойство range.methods функции range(). В таком способе хранения объекта-прототипа нет ничего необычного. Во-вторых, отметьте, что функция range() определяет свойства from и to для каждого объекта range. Эти не общие, не унаследованные свойства определяют уникальную информацию для каждого отдельного объекта range. Наконец, обратите внимание, что все общие, унаследованные методы, определяемые свойством range.methods, используют свойства from и to и ссылаются на них с помощью ключевого слова this, указывающего на объект, относительно которого вызываются эти методы. Такой способ использования this является фундаментальной характеристикой методов любого класса.
9.2. Классы и конструкторы
В примере 9.1 демонстрируется один из способов определения класса в языке JavaScript. Однако это не самый типичный способ, потому что он не связан с определением конструктора. Конструктор - это функция, предназначенная для инициализации вновь созданных объектов. Как описывалось в разделе 8.2.3, конструкторы вызываются с помощью ключевого слова new. Применение ключевого слова new при вызове конструктора автоматически создает новый объект, поэтому конструктору остается только инициализировать свойства этого нового объекта. Важной особенностью вызова конструктора является использование свойства prototype конструктора в качестве прототипа нового объекта. Это означает, что все объекты, созданные с помощью одного конструктора, наследуют один и тот же объект-прототип и, соответственно, являются членами одного и того же класса. В примере 9.2 демонстрируется, как можно было бы реализовать класс range, представленный в примере 9.1, не с помощью фабричной функции, а с помощью функции конструктора:
Пример 9.2. Реализация класса Range с помощью конструктора
// range2.js: Еще один класс, представляющий диапазон значений.
// Это функция-конструктор, которая инициализирует новые объекты Range.
// Обратите внимание, что она не создает и не возвращает объект.
// Она лишь инициализирует его.
function Range(from, to) {