Knockout中使用订阅(subscription)的方式来处理DOM元素的值改变的事件(当然修改数据源中的相应值也会触发)。在开发过程中,往往需要做DOM元素的联动效果,以往都是自己捕获DOM的change事件,现在有了Knockout的订阅特性,我们就只需要将我们的关注点放在对订阅的回调事件的处理逻辑上。
1.简单使用
在定义ViewModel的时候,指明ViewModel的属性subscribe的回调函数即可!
var Person = function() {
var self = this;
self.name = ko.observable("justin");
self.name.subscribe(function(newValue) {
alert("The person's new name is " + newValue);
});
};
var person = new Person();
//person.name("justin");
ko.applyBindings(person);
回调函数会传入一个参数,这个参数可以是简单对象,也可以是复杂对象。当数据源的值改变时(如person.name("justin")这句执行时,或者DOM上修改knockout所绑定元素的值的时候),这个subscribe方法就会自动执行。
<div style="margin-left: 10px;margin-top: 10px">
<input type="text" data-bind="value:name" />
</div>
这里我们绑定了一个input元素的值到Person对象的name属性,当name值发生改变时就会触发subscribe。
我们的默认值是justin,值修改之后,立即触发事件。如果我们需要与其他控件进行联动,这样做就很方便了。
2.多事件订阅
一个对象的某个属性其实可以订阅多个事件。
var test = ko.observable();
// create a subscription for the "test-event"
test.subscribe(function (val) {
console.log(val);
}, test, "test-event1");
test.subscribe(function (val) {
for (var i in val) {
console.log(val[i]);
}
//console.log(val[0]);
//console.log(val[1]);
}, test, "test-event2");
test.notifySubscribers("Hello World1", "test-event1");
test.notifySubscribers(["Hello","World"],"test-event2");
这里我们的一个属性订阅了两个事件,test-event1和test-event2,我们调用test.notifySubscribers方法来通知所有的订阅者,事件被调用了!
这里我们可以把test-event1和test-event2理解为订阅的主题(topic),我们针对不同的主题设定不同的回调事件!
下面的例子更为贴切:
// Dummy Subscribable
function PubSub() {
// inherit Subscribable
ko.subscribable.call(this);
}
// create an instance of our Subscribable
var pubsub = new PubSub();
// make a subscription
var subscription1 = pubsub.subscribe(function (val) {
console.log(val);
}, pubsub, 'test-topic1');
var subscription2 = pubsub.subscribe(function (val) {
console.log(val);
}, pubsub, 'test-topic2');
pubsub.notifySubscribers("hello topic1", "test-topic1");
pubsub.notifySubscribers("hello topic2", "test-topic2");
// console: "hello world"
// clean up things
subscription1.dispose();
subscription2.dispose();
这里需要脑补一点javascript的call方法:
call方法:
语法:call([thisObj[,arg1[, arg2[, [,.argN]]]]])
定义:调用一个对象的一个方法,以另一个对象替换当前对象。
说明:
call 方法可以用来代替另一个对象调用一个方法。call 方法可将一个函数的对象上下文从初始的上下文改变为由 thisObj 指定的新对象。
如果没有提供 thisObj 参数,那么 Global 对象被用作 thisObj。
其实PubSub可以理解为在ko.subscribe上封装了一层,代码subscribable对象去调用subscribe方法而已!
Obserable对象是knockout的核心,其简单实现如下:
// Very Simple Knockout Observable Implementation
// ko.observable is actually a function factory
ko.observable = function (initialValue) {
// private variable to hold the Observable's value
var _latestValue = initialValue;
// the actual "Observable" function
function observable() {
// one or more args, so it's a Write
if (arguments.length > 0) {
// set the private variable
_latestValue = arguments[0];
// tell any subscribers that things have changed
observable["notifySubscribers"](_latestValue);
return this; // Permits chained assignments
}
else { // no args, so it's a Read
// just hand back the private variable's value
return _latestValue;
}
}
// inherit from Subscribable
ko.subscribable.call(observable);
3.总结
knockout将发布订阅引入到MVVM的机制中,极大地丰富了对事件的处理功能,典型的观察者模式的运用,值得深入了解其原理。