角度表达式中的多个语句

时间:2013-07-08 14:49:02

标签: angularjs

我正在玩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分配给kkm,并不意味着会复制值,而是将这些名称绑定在一起。我能理解。但是如果我删除显示kjsFiddle)值的行,就会发生奇怪的事情:

<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的惯用方式,但我想真正理解表达式引擎,我无法解释这种行为。

1 个答案:

答案 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

摘要的进行如下:

  1. 评估{{ i = 42 }}scope.i设置为42,并显示&#34; 42&#34;在文件中。但是,由于范围已经改变,还有一个&#34; &#34;标志设置,这意味着我们将再次循环通过手表(见下面的步骤5)
  2. 评估{{ m = k; k = j; j = i+1; i }}scope.mscope.k设置为undefined,将scope.j评估为43,并显示&#34; 42&#34;在文件中。
  3. 评估{{ j }}显示&#34; 43&#34;在文件中。
  4. 评估{{ k }}然后{{ m }}只显示&#34;&#34;在文档中,因为它们目前尚未定义。
  5. 现在,由于设置了脏标志,我们重复所有这些手表;这次,当我们运行{{ m = k; k = j; j = i+1; i }}时,k = j起作用,因为j存在;所以k被赋值为43.
  6. 这次我们下到{{ k }}时,它的值已从undefined更改为2,因此脏标志再次设置,但现在文档正在显示&#34; k = 2&#34; 。但是,{{ m }}仍未定义。
  7. 就像5-6一样,我们再次通过手表,这次,{{ m = k; k = j; j = i+1; i }}导致m被设置为43。
  8. Tangent:双重处理手表?

    切向地,我在浏览摘要代码时遇到了一个有趣的事情:似乎脏标志在第一次通过手表时始终设置为真,因为从未检查过的手表没有记录&#34;最后&#34;值。看起来这样会导致大量不必要的双重处理。即使观察值是{{2}}之类的常数,这似乎也是如此。我在这里误解了什么吗?