js singleton
In software engineering, the singleton design pattern is used to restrict instantiation of a class to one (or a few) objects.Да, в js нет классов, но инкапсуляция поддерживается, потому примитивная реализация паттерна синглтона иногда может быть полезна. Например, если конструктор объекта выполняет кучу долгих (или необратимых) операций (например, с DOM'ом) и нет возможности проследить за инстанциацией вручную (что в такой неоднородной среде, как веб-приложение иногда бывает крайне сложно), удобно описать конструктор так, чтобы он выполнился лишь единожды.
В языках с классическим ООД синглтон реализуется просто:
- запрещается доступ к конструктору
- добавляется статическое свойство для хранения ссылки на объект
- добавляется статический метод, проверяющий соответствюущее свойство на наличие в нём ссылки на объект, в случае отсутствия инстанцирует объект, кладёт ссылку на него в свойство и возвращает
- по вкусу добавляются приправы вроде запрещения клонирования и и прочая
var singleton = function(){
var obj = function() { ...dosmth...};
var instance = new obj;
}
Можно сократить до:
var singleton = function(){
var instance = new function() { ...dosmth...};
}
Все бы хорошо, но пока что синглтоном тут и не пахнет — каждый раз при инстанциации объекта (var stmh = new singleton();) в его локальной области видимости анонимным конструктором(т.к. мы не указали имя, а просто написали new function(){}) будет создаваться объект, и соответственно всё, что прописано в его конструкторе, будет выполнено. Итак, следующим шагом будет «синглтонизация» нашего объекта. Наиболее простым вариантом будет вынести переменную, хранящую ссылку на инстанцированный объект вовне нашего конструктора синглтона.
var instance = false;
var singleton = function(){
if (!instance) instance = new function() { ...dosmth...};
return instance;
}
Для наглядности вместо dosmth подставим
var rnd = Math.random();
this.echo = function(){ alert(rnd); };
Т.е. добавим private-свойство и public-метод, если выражаться в терминах классического ООП.
Итого:
var instance = false;
var singleton = function(){
if (!instance) instance = new function() {
var rnd = Math.random();
this.echo = function(){ alert(rnd); };
};
return instance;
}
Всё вроде замечательно работает, но только инкапсуляция нарушена — используется глобальная переменная там, где ее быть не должно.
Статическое свойство удобнее всего описать с помощью свойства arguments.callee. Для примера считаем числа фибоначчи:
По-обычному:
function fib(x){
if (x == 0) return 0; if (x==1) return 1;
return fib(x-1)+fib(x-2);
}
for (var x=0; x<20; ++x) document.write(fib(x) + "<br>");
и посредством анонимной функции:
var fib = function(x) {
if (x == 0) return 0; if (x == 1) return 1;
return arguments.callee(x-1) + arguments.callee(x-2);
};
for (var x=0; x<20; ++x) document.write(fib(x) + "<br>");
Как видно, arguments.callee было задумано как свойство, хранящее ссылку на текущую функцию. Т.к. в js почти всё — объект, и к тому же присвоение несуществующему свойству значения создает это свойство, мы будем хранить ссылку на созданный объект в свежесозданном свойстве объекта arguments.callee.
var singleton = function(){
if (typeof arguments.callee.instance=='undefined'){
arguments.callee.instance = new function(){
var rnd = Math.random();
this.echo = function(){ alert(rnd); };
};
}
return arguments.callee.instance;
};
Усё, глобальная область видимости не пачкается, и синглтон во всей его красе. Сколько бы мы не инстанцировали объектов, rnd будет выведен один и тот же.
0 Comments:
Отправить комментарий
<< Home