现在,双向数据绑定是客户端应用程序的关键功能之一。 如果没有数据绑定,则每当模型发生更改时,开发人员都必须处理大量逻辑以将数据手动绑定到视图。 像Knockout , AngularJS和Ember这样的JavaScript库都支持双向绑定,但是这些库使用不同的技术来检测更改。
淘汰赛和灰烬使用可观察到的东西。 可观察对象是包装在模型对象属性周围的函数。 只要相应对象或属性的值发生更改,就会调用这些函数。 尽管这种方法行之有效,并且可以检测并通知所有更改,但它却剥夺了使用纯JavaScript对象的自由,因为现在我们必须处理函数。
Angular使用脏检查来检测更改。 这种方法不会污染模型对象。 它为添加到模型中的每个对象注册观察者。 只要Angular的摘要周期开始并且数据有任何更改,就会执行所有这些观察程序。 这些更改由相应的观察者处理。 该模型仍然是一个普通对象,因为没有围绕它创建包装器。 但是,随着观察者数量的增加,此技术会导致性能下降。
什么是Object.observe
?
Object.observe
(又名Oo
)是作为ECMAScript 7的一部分添加到JavaScript的功能,以支持浏览器中本机的对象更改检测。 尽管ES7尚未完成,但是基于闪烁的浏览器(Chrome和Opera)已经支持此功能。
由于Object.observe
将由浏览器本地支持,并且可以直接在对象上运行而无需在其周围创建任何包装,因此该API既易于使用,又是性能的双赢。 如果浏览器支持Object.observe
则可以实现双向绑定,而无需外部库。 这并不意味着一旦实现Oo
,所有现有的双向绑定库都将Oo
。 在使用Oo
检测到更改后,我们仍然需要它们有效地更新UI。 此外,如果不是所有目标浏览器都支持Oo
,则库将在内部填充更改检测的逻辑。
观察对象的属性
现在,您已经了解了Oo
的优点,让我们来看一下它的实际作用。
observe()
方法是在Object
定义的异步静态方法。 它可用于查找对象的更改,它接受三个参数:
- 要观察的对象
- 检测到更改时要调用的回调函数
- 一个可选数组,其中包含要注意的更改类型
让我们看一个使用该方法的例子。 考虑以下代码段:
var person = {
name: 'Ravi',
country: 'India',
gender: 'Male'
};
function observeCallback(changes){
console.log(changes);
};
Object.observe(person, observeCallback);
person.name = 'Rama'; // Updating value
person.occupation = 'writer'; // Adding a new property
delete person.gender; // Deleting a property
在这段代码中,我们创建了带有一些数据的对象文字。 我们还定义了一个名为observeCallback()
的函数,该函数将用于记录对象的更改。 然后,我们开始使用Oo
观察变化。 最后,我们对对象进行了一些更改。
如果您在控制台上看到输出,您将看到检测到并记录了所有三个更改。 以下屏幕截图显示了代码段产生的结果:
Oo
异步运行,它将所有发生的更改分组,并在调用时将它们传递给回调。 因此,这里我们收到了三个应用于对象的三个更改的条目。 如您所见,每个条目都包括更改后的属性的名称,旧值,更改的类型以及带有新值的对象本身。
下面报告了先前代码的实时演示(请记住打开控制台以查看结果):
请参阅CodePen上的SitePoint( @SitePoint )提供的Pen emKveB 。
在我们的代码中,我们没有指定要查找的更改类型,因此它会观察添加,更新和删除。 可以使用observe
方法的第三个参数来控制它,如下所示:
Object.observe(person, observeCallback, ['add', 'update']);
注册通知
observe()
方法能够检测对添加到对象的直接属性所做的更改。 它无法检测使用getter和setter创建的属性的更改。 由于这些属性的行为由作者控制,因此更改检测也必须归作者所有。 要解决此问题,我们需要使用一个通告程序(可通过Object.getNotifier()
)来通知对该属性所做的更改。
考虑以下代码段:
function TodoType() {
this.item = '';
this.maxTime = '';
var blocked = false;
Object.defineProperty(this, 'blocked', {
get:function(){
return blocked;
},
set: function(value){
Object.getNotifier(this).notify({
type: 'update',
name: 'blocked',
oldValue: blocked
});
blocked = value;
}
});
}
var todo = new TodoType();
todo.item = 'Get milk';
todo.maxTime = '1PM';
console.log(todo.blocked);
Object.observe(todo, function(changes){
console.log(changes);
}, ['add', 'update']);
todo.item = 'Go to office';
todo.blocked = true;
TodoType
是具有两个属性的构造函数。 除了它们之外, blocked
使用加入Object.defineProperty
。 在我们的示例中,为此属性定义的设置器很简单。 在典型的业务应用程序中,它可能会执行一些验证,并且在验证失败的情况下可能不会设置值。 但是,我想保持简单。
最后,您可以看到在我们的示例中,仅当有更新时才发送通知。
对blocked
的属性所做的更改在Chrome开发人员工具中产生以下结果:
下面是该示例的实时演示(请记住打开控制台以查看结果):
请参阅CodePen上 SitePoint( @SitePoint )的Pen NPzgOO 。
观察多个变化
有时,在以某种方式修改了两个或更多属性之后,我们可能需要运行计算。 尽管我们可以使用通知程序分别通知这两个更改,但最好发送一个带有自定义类型名称的通知,以表明两个值均已修改。 这可以使用notifier.performChange()
方法完成。 此方法接受三个参数:
- 自定义类型的名称
- 回调函数执行更改。 从此函数返回的值用于更改对象
- 应用更改的对象
让我们向上面定义的类TodoType
添加一个名为done
的新属性。 此属性的值指定待办事项是否已完成。 当值done
设置为true
,我们需要设置属性的值blocked
,以true
为好。
以下代码段定义了此属性:
var done = false;
Object.defineProperty(this, 'done', {
get: function(){
return done;
},
set: function(value){
if(value){
var notifier = Object.getNotifier(this);
if(blocked && value) {
notifier.performChange('doneAndUnblocked', function(){
done = value;
blocked = false;
return { oldDone: false, oldBlocked: true };
}, this);
}
else{
notifier.notify({
type: 'update',
name: 'done',
oldValue: done
});
done = value;
}
}
}
});
一旦执行performChange
的回调内的逻辑,将通过传入的自定义更改类型来通知更改。 默认情况下, Object.observe
不会观察到此类型。 我们需要明确要求Oo
观察自定义类型的更改。 以下代码段显示了todo
对象上的修改后的Oo
,以观察自定义类型以及添加和更新类型的更改:
Object.observe(todo, function(changes){
console.log(changes);
}, ['add', 'update', 'doneAndUnblocked']);
todo.blocked = true;
todo.done = true;
上面的代码片段在将done
设置为true
之前,将blocked的值设置为true
。 因此,它将发送具有自定义更改类型的通知。 以下屏幕快照显示了自定义类型返回的更改对象的详细信息:
下面是该示例的实时演示(请记住打开控制台以查看结果):
请参阅CodePen上的SitePoint( @SitePoint )的笔yyEXGd 。
观察阵列
观察数组类似于观察对象。 唯一的区别是观察者功能必须使用Array.observe
而不是Object.observe
注册。 以下代码段演示了这一点:
var array = ['morning', 'Afternoon', 'Evening'];
var arrayObserver = function(changes){
console.log(changes);
};
Array.observe(array, arrayObserver);
array[0] = 'Morning';
array.push('Night');
array.splice(1, 1);
下面是该示例的实时演示(请记住打开控制台以查看结果):
见笔GgGEzQ由SitePoint( @SitePoint上) CodePen 。
删除注册的观察者
可以分别使用Object.unobserve()
或Array.unobserve()
删除对象或数组上已注册的观察者。 此方法接受两个参数,对象或数组以及要删除的回调。 因此,要使用此方法,我们需要引用回调。
Object.unobserve(person, observeCallback);
结论
一旦所有浏览器都完全支持Oo
,所有客户端库中的更改检测都将标准化。 Aurelia已经开始使用它, Angular 2的更改检测库watchtower.js在内部使用Oo
, Ember将来还将使用它进行更改检测。 Angular 2和Aurelia实施了pollyfills,以在Oo
本身不可用时回退。
有了这些强大的浏览器,围绕客户端双向绑定的未来将更加光明。 让我们期待其他浏览器早日赶上!
From: https://www.sitepoint.com/introduction-object-observe/