关于javascript:AngularJS:$ observe和$ watch方法之间的区别

AngularJS : Difference between the $observe and $watch methods

我知道WatchersObservers都是在$scope的角度发生变化时计算出来的。但不明白这两者到底有什么区别。

我最初的理解是,Observers是为角表达式计算的,这些角表达式是HTML端的条件,当执行$scope.$watch()函数时,执行Watchers。我想得对吗?


$observer()是attributes对象上的一个方法,因此,它只能用于观察/监视dom属性的值更改。它只在指令内部使用/调用。当需要观察/观察包含插值(即123; s)的DOM属性时,请使用$observer。例如,attr1="Name: {{name}}",然后在一个指令中:attrs.$observe('attr1', ...)。(如果你尝试使用scope.$watch(attrs.attr1, ...),它将不起作用,因为s——你将得到undefined)。使用$watch来处理其他所有事情。

$watch()更复杂。它可以观察/观察"表达式",其中表达式可以是函数或字符串。如果表达式是一个字符串,它是$parse'd(即,作为一个角表达式计算)到一个函数中。(每个摘要循环都调用这个函数。)字符串表达式不能包含s。$watch是作用域对象上的一个方法,因此可以在您有权访问作用域对象的任何位置使用/调用它,因此在

  • 一个控制器——任何控制器——通过ng视图、ng控制器或指令控制器创建的控制器。
  • 指令中的链接函数,因为它也可以访问范围

因为字符串的计算是角度表达式,所以当您希望观察/观察模型/范围属性时,通常使用$watch。例如,attr1="myModel.some_prop",然后在控制器或链路功能中:scope.$watch('myModel.some_prop', ...)scope.$watch(attrs.attr1, ...)scope.$watch(attrs['attr1'], ...)。(如果您尝试attrs.$observe('attr1'),您将得到字符串myModel.some_prop,这可能不是您想要的。)

正如对@primosk答案的评论中所讨论的,所有$observers和$watches在每个摘要循环中都会被检查。

带有隔离作用域的指令更加复杂。如果使用了"@"语法,则可以$observer或$watch包含插值的dom属性(即,s)。(它与$watch一起使用的原因是,@'语法为我们做了插值,因此$watch看到一个没有s的字符串)为了更容易记住在什么时候使用,我建议在这种情况下也使用$watch。

为了帮助测试所有这些,我编写了一个punker,它定义了两个指令。一个(d1不创建新作用域,另一个(d2创建独立作用域。每个指令具有相同的六个属性。每个属性都是$observer'd和$watch'ed。

1
2
<div d1 attr1="{{prop1}}-test" attr2="prop2" attr3="33" attr4="'a_string'"
        attr5="a_string" attr6="{{1+aNumber}}">

查看控制台日志,查看链接函数中$observe和$watch之间的差异。然后单击链接,查看哪些$observers和$watches是由单击处理程序所做的属性更改触发的。

注意,当link函数运行时,包含的任何属性都不会被评估(因此,如果尝试检查这些属性,您将得到undefined)。查看插值值的唯一方法是使用$observer(如果使用带"@"的隔离范围,则使用$watch)。因此,获取这些属性的值是一个异步操作。(这就是为什么我们需要$observer和$watch功能。)

有时你不需要$observe或$watch。例如,如果属性包含数字或布尔值(不是字符串),只需计算一次:attr1="22",然后在(例如)链接函数中:var count = scope.$eval(attrs.attr1)。如果它只是一个常量字符串&ndash;attr1="my string"&ndash;,那么只需在指令中使用attrs.attr1(不需要$eval())。

另请参见Vojta的Google Group帖子关于$watch表达式。


如果我正确理解你的问题,你会问如果你用$watch注册监听回调,或者用$observe注册监听回调有什么不同。

当执行$digest时,将激发用$watch注册的回调。

当包含插值的属性(如attr="{{notJetInterpolated}}"的值发生变化时,调用注册到$observe的回调。

在指令中,您可以以非常相似的方式使用这两种方法:

1
2
3
    attrs.$observe('attrYouWatch', function() {
         // body
    });

1
2
3
    scope.$watch(attrs['attrYouWatch'], function() {
         // body
    });


我认为这很明显:

  • $observe用于链接指令的函数。
  • $watch用于监视作用域值的任何变化。

记住:两个函数都有两个参数,

1
$observe/$watch(value : string, callback : function);
  • 值:始终是对被监视元素的字符串引用(作用域变量的名称或要监视的指令属性的名称)
  • 回调:执行function (oldValue, newValue)形式的函数。

我做了一个PLUNKER,所以您实际上可以了解它们的使用情况。我用变色龙作类比,以便更容易描绘。


为什么$observe与$watch不同?

在每个digest()循环中,将对watchExpression进行计算并与前一个值进行比较,如果watchExpression值发生变化,则调用watch函数。

$观察特定于观察插值值。如果指令的属性值被插入,例如dir-attr="{{ scopeVar }}",则只有在设置了插入值时(因此当$digest已经确定需要进行更新时),才会调用观察函数。基本上,已经有了一个观察插值的人,$observer函数将其作为基础。

请参见compile.js中的$observer&;$set