我正在玩Angularjs,可能我在滥用它。我使用分号在像这样的角度表达式中有几个语句(jsFiddle):
<tr ng-repeat="i in [1, 2, 3, 4]">
<td>i = {{ m = k; k = j; j = i + 1; i}}</td>
<td>j = {{j}}</td>
<td>k = {{k}}</td>
<td>m = {{m}}</td>
</tr>
首先,我认为k
在计算j
之前会有i+1
的值,但显然它不会像这样工作。结果是:
i = 1 j = 2 k = 2 m = 2
i = 2 j = 3 k = 3 m = 3
i = 3 j = 4 k = 4 m = 4
i = 4 j = 5 k = 5 m = 5
所以显然将j
分配给k
和k
到m
,并不意味着会复制值,而是将这些名称绑定在一起。我能理解。但是如果我删除显示k
(jsFiddle)值的行,就会发生奇怪的事情:
<tr ng-repeat="i in [1, 2, 3, 4]">
<td>i = {{ m = k; k = j; j = i + 1; i}}</td>
<td>j = {{j}}</td>
<td>m = {{m}}</td>
</tr>
我正在获得:
i = 1 j = 2 m =
i = 2 j = 3 m =
i = 3 j = 4 m =
i = 4 j = 5 m =
也就是说,m
不包含任何值,尽管它已绑定到j
(通过k
)。这可能是因为k
本身未被评估。
我的问题是:它不是AngularJS中的错误吗?当然k
是否在绑定链中,即使它没有直接显示,也应该进行评估。或者我误解了什么?
我知道它可能不是使用AngularJS的惯用方式,但我想真正理解表达式引擎,我无法解释这种行为。
答案 0 :(得分:8)
这里有几个相互作用的问题。
首先,您的陈述是向后排序的:您在设置j之前设置k = j,这导致其未定义。
其次,更重要的是,插值表达式(&#34; {{}}&#34;中的表达式)不应该用于更改范围的状态。这有一个很好的理由:
插值的工作方式是,当它编译你的html时,angular会在每个插值表达式上注册scope.$watch
。
但是这些观察过的表达式在摘要期间可以多次执行:每当侦听器修改范围时,它会导致angular再次通过该范围上的监视。出于这个原因,观看的表达式确实应该是#elempotent&#34;即它们应该没有副作用/导致状态没有变化。这是来自documentation for $watch
:
每次调用$ digest()时都会调用watchExpression,并应返回将要监视的值。 (由于$ digest()在检测到更改时重新运行,因此每个$ digest()可以多次执行watchExpression,并且应该是幂等的。)
具体来说,这是您示例中的内容。首先,它与转发器无关。每个ng-repeat
项都有自己的范围,所以这里发生的事情等同于这个更简单的例子:
<div ng-app>
{{ i = 42 }}<br>
i = {{ m = k; k = j; j = i+1; i }}<br>
j = {{j}}<br>
k = {{k}}<br>
m = {{m}}<br>
</div>
(此处为fiddle)
摘要的进行如下:
{{ i = 42 }}
将scope.i
设置为42,并显示&#34; 42&#34;在文件中。但是,由于范围已经改变,还有一个&#34; 脏&#34;标志设置,这意味着我们将再次循环通过手表(见下面的步骤5){{ m = k; k = j; j = i+1; i }}
将scope.m
和scope.k
设置为undefined
,将scope.j
评估为43,并显示&#34; 42&#34;在文件中。 {{ j }}
显示&#34; 43&#34;在文件中。 {{ k }}
然后{{ m }}
只显示&#34;&#34;在文档中,因为它们目前尚未定义。{{ m = k; k = j; j = i+1; i }}
时,k = j起作用,因为j存在;所以k被赋值为43. {{ k }}
时,它的值已从undefined更改为2,因此脏标志再次设置,但现在文档正在显示&#34; k = 2&#34; 。但是,{{ m }}
仍未定义。{{ m = k; k = j; j = i+1; i }}
导致m被设置为43。切向地,我在浏览摘要代码时遇到了一个有趣的事情:似乎脏标志在第一次通过手表时始终设置为真,因为从未检查过的手表没有记录&#34;最后&#34;值。看起来这样会导致大量不必要的双重处理。即使观察值是{{2}}之类的常数,这似乎也是如此。我在这里误解了什么吗?