Kendo UI - Observable

时间:2024-01-16 14:27:26

在 Kendo 中,基类 Class 第一个重要的派生类就是 Observable, 顾名思义,就是一个可观察的对象,也就是观察者模式的基础。

对于观察者模式来说,应该有主题和观察者,这里我们讨论的其实是主题,观察者只需要提供一个回调函数,在适当的时候得到回调就可以了。

对于主题来说,我们应该支持多种观察的目标,如果你使用过 .NET 的事件,这里简直就是将 .NET 的事件*重新实现了一下。

1.事件

_events 是用来保存注册的事件信息的存储对象,可以在主题上定义多种事件,每个事件就是 _events 上的一个字段,字段的名字就是事件名称,值是一个数组,用来保存注册到这个事件的回调函数。初始化函数中,将这个对象创建出来。

init: function() {
this._events = {};
},

2. 注册

bind 函数用来进行注册,既可以使用事件名称,处理器方式,也可以一次性注册多个事件,使用事件名称的数组,对应每个事件的处理函数对象来表示。

最关键的实际上是这两行。

 events = that._events[eventName] = that._events[eventName] || [];
events.push(handler);

这里有一些特殊的处理,就是可以注册仅仅执行一次的处理器。在注册的时候,需要将 one 设置为 true,默认是 undefined,也就是多次的。

在一次的情况下,会自动将用户注册的处理器另外保存到 original 中,然后创建一个新的处理器进行注册,这个处理器在执行一次之后,自动将自己从处理器列表中删除。

注册的全部代码

bind: function(eventName, handlers, one) {
var that = this,
idx,
eventNames = typeof eventName === STRING ? [eventName] : eventName,
length,
original,
handler,
handlersIsFunction = typeof handlers === FUNCTION,
events; if (handlers === undefined) {
for (idx in eventName) {
that.bind(idx, eventName[idx]);
}
return that;
} for (idx = 0, length = eventNames.length; idx < length; idx++) {
eventName = eventNames[idx]; handler = handlersIsFunction ? handlers : handlers[eventName]; if (handler) {
if (one) {
original = handler;
handler = function() {
that.unbind(eventName, handler);
original.apply(that, arguments);
};
handler.original = original;
}
events = that._events[eventName] = that._events[eventName] || [];
events.push(handler);
}
} return that;
},

3. 取消注册

对应注册的就是取消注册了。

unbind 完成取消注册的任务,取消注册的时候,有三种选择

  • 全部取消注册的观察者
  • 将某个时间的观察者取消
  • 或者单个取消

所以代码更加简单明了。original 就是在一次性事件中保存的原有处理器。

unbind: function(eventName, handler) {
var that = this,
events = that._events[eventName],
idx; if (eventName === undefined) {
that._events = {};
} else if (events) {
if (handler) {
for (idx = events.length - 1; idx >= 0; idx--) {
if (events[idx] === handler || events[idx].original === handler) {
events.splice(idx, 1);
}
}
} else {
that._events[eventName] = [];
}
} return that;
}

4. 触发处理

触发就比较容易了,提供事件的名称,事件的参数就可以了,直接遍历数组中保存的每一个处理器,通过 call 调用将对象自己作为 this 传递到处理器中。

trigger: function(eventName, e) {
var that = this,
events = that._events[eventName],
idx,
length; if (events) {
e = e || {}; e.sender = that; e._defaultPrevented = false; e.preventDefault = preventDefault; e.isDefaultPrevented = isDefaultPrevented; events = events.slice(); for (idx = 0, length = events.length; idx < length; idx++) {
events[idx].call(that, e);
} return e._defaultPrevented === true;
} return false;
}

5. 辅助函数

额外还提供了两个辅助函数,one 和 first

one 用来检查注册仅仅执行一次的处理器,你会看到通过直接将 bind 的 one 参数设置为 true 来实现的。

one: function(eventNames, handlers) {
return this.bind(eventNames, handlers, true);
},

first 用来将处理函数压入调用对象的最前面, unshift() 方法可向数组的开头添加一个或更多元素,并返回新的长度。

first: function(eventName, handlers) {
var that = this,
idx,
eventNames = typeof eventName === STRING ? [eventName] : eventName,
length,
handler,
handlersIsFunction = typeof handlers === FUNCTION,
events; for (idx = 0, length = eventNames.length; idx < length; idx++) {
eventName = eventNames[idx]; handler = handlersIsFunction ? handlers : handlers[eventName]; if (handler) {
events = that._events[eventName] = that._events[eventName] || [];
events.unshift(handler);
}
} return that;
},

6. 全部代码

全部代码如下:

var Observable = Class.extend({
init: function() {
this._events = {};
}, bind: function(eventName, handlers, one) {
var that = this,
idx,
eventNames = typeof eventName === STRING ? [eventName] : eventName,
length,
original,
handler,
handlersIsFunction = typeof handlers === FUNCTION,
events; if (handlers === undefined) {
for (idx in eventName) {
that.bind(idx, eventName[idx]);
}
return that;
} for (idx = 0, length = eventNames.length; idx < length; idx++) {
eventName = eventNames[idx]; handler = handlersIsFunction ? handlers : handlers[eventName]; if (handler) {
if (one) {
original = handler;
handler = function() {
that.unbind(eventName, handler);
original.apply(that, arguments);
};
handler.original = original;
}
events = that._events[eventName] = that._events[eventName] || [];
events.push(handler);
}
} return that;
}, one: function(eventNames, handlers) {
return this.bind(eventNames, handlers, true);
}, first: function(eventName, handlers) {
var that = this,
idx,
eventNames = typeof eventName === STRING ? [eventName] : eventName,
length,
handler,
handlersIsFunction = typeof handlers === FUNCTION,
events; for (idx = 0, length = eventNames.length; idx < length; idx++) {
eventName = eventNames[idx]; handler = handlersIsFunction ? handlers : handlers[eventName]; if (handler) {
events = that._events[eventName] = that._events[eventName] || [];
events.unshift(handler);
}
} return that;
}, trigger: function(eventName, e) {
var that = this,
events = that._events[eventName],
idx,
length; if (events) {
e = e || {}; e.sender = that; e._defaultPrevented = false; e.preventDefault = preventDefault; e.isDefaultPrevented = isDefaultPrevented; events = events.slice(); for (idx = 0, length = events.length; idx < length; idx++) {
events[idx].call(that, e);
} return e._defaultPrevented === true;
} return false;
}, unbind: function(eventName, handler) {
var that = this,
events = that._events[eventName],
idx; if (eventName === undefined) {
that._events = {};
} else if (events) {
if (handler) {
for (idx = events.length - 1; idx >= 0; idx--) {
if (events[idx] === handler || events[idx].original === handler) {
events.splice(idx, 1);
}
}
} else {
that._events[eventName] = [];
}
} return that;
}
});

7. 总结

Observable 提供了基本的观察者模式支持。