Приятно наблюдать за тем как развиваются языки программирования, за появлением различных фреймворков и паттернов. Так в моем любимом JavaScript в последнее время появилась хорошая тенденция оформлять код в виде модулей (классов). Это обеспечивает и лучшую читаемость программ и повышает повторное использование кода.
Разделение функционала на отдельные модули требует от программиста обеспечить их коммуникацию между собой. Одним из способов обеспечения такой связи служит система оповещения модулей, заинтересованных в получении информации о каких-то событиях, происходящих в других модулях.
/**
* Observable Object
* Just call it in constructor of your class (delegate pattern)
* Ex: var me = Observable(); // create new object
* or
* Observable(me); // extend existing object
*/
var Observable = function (target) {
// callback holder
var observers = {};
// create new object if needed
target = target || {};
target.attachObserver = function (eventType, callback) {
if (!observers[eventType]) {
observers[eventType] = [];
}
observers[eventType].push(callback);
return this;
};
target.detachObserver = function (eventType, callback) {
var a, i;
if (eventType) {
if (observers[eventType] && callback) {
a = observers[eventType];
for (i = 0; i < a.length; i++) {
if (a[i] === callback) {
a.splice(i, 1);
break;
}
}
} else {
delete observers[eventType];
}
} else {
observers = {};
}
return this;
};
target.notify = function () {
var a, i, handler, data = [];
// copy notification arguments
Array.prototype.push.apply(data, arguments);
a = observers[data.shift()];
if (a && a.length > 0) {
for (i = 0; i < a.length; i++) {
handler = a[i];
if (typeof handler === 'string') {
handler = this[handler];
}
if (handler instanceof Function) {
handler.apply(this, data);
}
}
}
return this;
};
return target;
};
Этот компонент при инициализации создает в указанном объекте 3 метода:
- attachObserver — добавляет колбек для определенного события
- detachObserver — удаляет один или все колбеки для определенного события
- notify — инициализирует событие, при этом вызываются все зарегистрированные колбеки с указанными аргументами
Все указанные методы поддерживают цепочный вызов.
var FieldObject = function (root) {
var me = Observable(this);
$(root).append('<div class="field"><input type="text" value="" /></div>');
$('div.field input', root).change(function () {
me.notify('valueDidChange', this.value);
});
return me;
};
FieldObject($('body'))
.attachObserver('valueDidChange', function (data) {
alert(data);
});
В этом примере я создал простой программный компонент FieldObject, которой добавляет на страницу текстовое поле, а затем подписался на событие valueDidChange , которое случается при обновлении этого текстового поля. Весь фокус в том, что я могу не знать, как устроен этот объект, и откуда получить значение его текстового поля. Все что мне нужно – это отреагировать на изменение поля.