Статические и фабричные методы
Материал на этой странице устарел, поэтому скрыт из оглавления сайта.
Более новая информация по этой теме находится на странице https://learn.javascript.ru/static-properties-methods.
Методы и свойства, которые не привязаны к конкретному экземпляру объекта, называют «статическими». Их записывают прямо в саму функцию-конструктор.
Статические свойства
В коде ниже используются статические свойства Article.count и Article.DEFAULT_FORMAT :
function Article() < Article.count++; >Article.count = 0; // статическое свойство-переменная Article.DEFAULT_FORMAT = "html"; // статическое свойство-константа
Они хранят данные, специфичные не для одного объекта, а для всех статей целиком.
Как правило, это чаще константы, такие как формат «по умолчанию» Article.DEFAULT_FORMAT .
Статические методы
С примерами статических методов мы уже знакомы: это встроенные методы String.fromCharCode, Date.parse.
Создадим для Article статический метод Article.showCount() :
function Article() < Article.count++; //. >Article.count = 0; Article.showCount = function() < alert( this.count ); // (1) >// использование new Article(); new Article(); Article.showCount(); // (2)
Здесь Article.count – статическое свойство, а Article.showCount – статический метод.
Обратим внимание на использование this в примере выше. Несмотря на то, что переменная и метод – статические, он всё ещё полезен. В строке (1) он равен Article .
Пример: сравнение объектов
Ещё один хороший способ применения – сравнение объектов.
Например, у нас есть объект Journal для журналов. Журналы можно сравнивать – по толщине, по весу, по другим параметрам.
Объявим «стандартную» функцию сравнения, которая будет сравнивать по дате издания. Эта функция сравнения, естественно, не привязана к конкретному журналу, но относится к журналам вообще.
Поэтому зададим её как статический метод Journal.compare :
function Journal(date) < this.date = date; // . >// возвращает значение, большее 0, если A больше B, иначе меньшее 0 Journal.compare = function(journalA, journalB) < return journalA.date - journalB.date; >;
В примере ниже эта функция используется для поиска самого раннего журнала из массива:
function Journal(date) < this.date = date; this.formatDate = function(date) < return date.getDate() + '.' + (date.getMonth() + 1) + '.' + date.getFullYear(); >; this.getTitle = function() < return "Выпуск от " + this.formatDate(this.date); >; > Journal.compare = function(journalA, journalB) < return journalA.date - journalB.date; >; // использование: var journals = [ new Journal(new Date(2012, 1, 1)), new Journal(new Date(2012, 0, 1)), new Journal(new Date(2011, 11, 1)) ]; function findMin(journals) < var min = 0; for (var i = 0; i < journals.length; i++) < // используем статический метод if (Journal.compare(journals[min], journals[i]) >0) min = i; > return journals[min]; > alert( findMin(journals).getTitle() );
Статический метод также можно использовать для функций, которые вообще не требуют наличия объекта.
Например, метод formatDate(date) можно сделать статическим. Он будет форматировать дату «как это принято в журналах», при этом его можно использовать в любом месте кода, не обязательно создавать журнал.
function Journal() < /*. */ >Journal.formatDate = function(date) < return date.getDate() + '.' + (date.getMonth()+1) + '.' + date.getFullYear(); >// ни одного объекта Journal нет, просто форматируем дату alert( Journal.formatDate(new Date) );
Фабричные методы
Рассмотрим ситуацию, когда объект нужно создавать различными способами. Например, это реализовано во встроенном объекте Date. Он по-разному обрабатывает аргументы разных типов:
- new Date() – создаёт объект с текущей датой,
- new Date(milliseconds) – создаёт дату по количеству миллисекунд milliseconds ,
- new Date(year, month, day . ) – создаёт дату по компонентам год, месяц, день…
- new Date(datestring) – читает дату из строки datestring
«Фабричный статический метод» – удобная альтернатива такому конструктору. Так называется статический метод, который служит для создания новых объектов (поэтому и называется «фабричным»).
Пример встроенного фабричного метода – String.fromCharCode(code). Этот метод создаёт строку из кода символа:
var str = String.fromCharCode(65); alert( str ); // 'A'
Но строки – слишком простой пример, посмотрим что-нибудь посложнее.
Допустим, нам нужно создавать объекты User : анонимные new User() и с данными new User() .
Можно, конечно, создать полиморфную функцию-конструктор User :
function User(userData) < if (userData) < // если указаны данные -- одна ветка if this.name = userData.name; this.age = userData.age; >else < // если не указаны -- другая this.name = 'Аноним'; >this.sayHi = function() < alert(this.name) >; // . > // Использование var guest = new User(); guest.sayHi(); // Аноним var knownUser = new User(< name: 'Вася', age: 25 >); knownUser.sayHi(); // Вася
Подход с использованием фабричных методов был бы другим. Вместо разбора параметров в конструкторе – делаем два метода: User.createAnonymous и User.createFromData .
function User() < this.sayHi = function() < alert(this.name) >; > User.createAnonymous = function() < var user = new User; user.name = 'Аноним'; return user; >User.createFromData = function(userData) < var user = new User; user.name = userData.name; user.age = userData.age; return user; >// Использование var guest = User.createAnonymous(); guest.sayHi(); // Аноним var knownUser = User.createFromData(< name: 'Вася', age: 25 >); knownUser.sayHi(); // Вася
Преимущества использования фабричных методов:
- Лучшая читаемость кода. Как конструктора – вместо одной большой функции несколько маленьких, так и вызывающего кода – явно видно, что именно создаётся.
- Лучший контроль ошибок, т.к. если в createFromData ничего не передали, то будет ошибка, а полиморфный конструктор создал бы анонимного посетителя.
- Удобная расширяемость. Например, нужно добавить создание администратора, без аргументов. Фабричный метод сделать легко: User.createAdmin = function() < . >. А для полиморфного конструктора вызов без аргумента создаст анонима, так что нужно добавить параметр – «тип посетителя» и усложнить этим код.
Поэтому полиморфные конструкторы лучше использовать там, где нужен именно полиморфизм, т.е. когда непонятно, какого типа аргумент передадут, и хочется в одном конструкторе охватить все варианты.
А в остальных случаях отличная альтернатива – фабричные методы.
Итого
Статические свойства и методы объекта удобно применять в следующих случаях:
- Общие действия и подсчёты, имеющие отношения ко всем объектам данного типа. В примерах выше это подсчёт количества.
- Методы, не привязанные к конкретному объекту, например сравнение.
- Вспомогательные методы, которые полезны вне объекта, например для форматирования даты.
- Фабричные методы.
Задачи
Счётчик объектов
Добавить в конструктор Article :
- Подсчёт общего количества созданных объектов.
- Запоминание даты последнего созданного объекта.
Используйте для этого статические свойства.
Пусть вызов Article.showStats() выводит то и другое.
function Article() < this.created = new Date(); // . ваш код . >new Article(); new Article(); Article.showStats(); // Всего: 2, Последняя: (дата) new Article(); Article.showStats(); // Всего: 3, Последняя: (дата)