09. Функции. Часть 3. Конструкторы

Расширение и модификация встроенных классов

Не только классы, создаваемые пользователем, имеют прототипы. Их имеют и встроенные классы, такие как String, Number, Array, Date и все другие встроенные классы базового JavaScript.

Объекты клиентского JavaScript, такие как window, document и другие, относящиеся к объектной модели браузера и объектной модели гипертекстового документа, прототипами не обладают.

Пользуясь этим, можно расширять и модифицировать (перекрывать) методы встроенных классов. Покажем на примере класса String, как это можно делать.

Добавим в прототип класса String метод delBlanks, который будет удалять из строки начальные и конечные пробелы:


String.prototype.delBlanks = function () 
  {return this.replace(/^\s+|\s+$/g,'');}

Применение replace к this кажется немного странным. Но все законно! Что такое this в конструкторе String? Это ссылка на создаваемый объект. Мы не знаем имени свойства конструктора, которое содержит саму строку, поэтому используем ссылку не на неё, а на сам объект. И это проходит, ибо, когда объект используется в контексте строки, он автоматически преобразуется в строку при помощи метода toString. Значит, запись this.replace(/^\s+|\s+$/g,'') эквивалента записи this.toString().replace(/^\s+|\s+$/g, '').

Пусть программа для исполнителя Кукарача задана в виде строки. К ней теперь можно применять созданный в прототипе класса String метод delBlanks:


var str = "    ВПРАВО ВЛЕВО ВНИЗ     ";
str = str.delBlanks(); // "ВПРАВО ВЛЕВО ВНИЗ"

Замечание 1. Вспомним, что строки в JavaScript относятся к элементарному типу данных (то есть не являются объектами). Но всякий раз, когда в контексте работы со строками появляется объектная нотация, создается объект-обёртка — экземпляр класса String, а после выполнения операции созданный объект уничтожается.

Замечание 2. На самом деле добавление в прототипы встроенных классов новых методов, а ещё хуже, перекрытие стандартных, является плохой практикой, ибо будет вводить в заблуждения коллег, читающих наш код. Разве есть в языке такой метод для работы со строками? — Будут думать они, увидев в коде запись str.delBlanks(). Если всё же хочется дополнить прототипы встроенных классов собственными методами, не жалейте комментариев и выносите определения в начало кода, чтобы они не потерялись в его глубинах.

Рассмотрим пример работы с прототипом класса Date.


var d = new Date(); // Создали объект -- экземпляр класса Date

А теперь добавим к созданному объекту метод myGetDay, который будет возвращать день недели по-русски:


d.myGetDay = function () // Вернуть день недели по-русски
{
  var dayNames = ["воскресенье", "понедельник", "вторник",
                   "среда", "четверг", "пятница", "суббота"];
  return dayNames[this.getDay()];
}
alert(d.myGetDay()); // Получилось "воскресенье", так как я запустил 
                     // этот код 27 июня 2010 года

Конечно, другой экземпляр, например,


var d1 = new Date();

ничего про метод объекта d не знает, и если написать:


var x = d1.myGetDay();

браузер выдаст сообщение об ошибке.

Смотрите пример «День по-русски».

А вот если добавить метод к прототипу класса Date, он станет доступен всем экземплярам этого класса.


Date.prototype.myGetDay  = function () // Вернуть день недели по-русски
{
  // Более компактный вариант записи кода:
  return ["воскресенье", "понедельник", "вторник",
          "среда", "четверг", "пятница", "суббота"][this.getDay()];
}
var d = new Date();   // Создали объект -- экземпляр класса Date
alert(d.myGetDay());  // Получилось "воскресенье"
var d1 = new Date();  // Создали объект -- экземпляр класса Date
alert(d1.myGetDay()); // И здесь получилось "воскресенье"

Смотрите пример «День по-русски 2».