7

我在玩 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会有 的值,但显然它不是这样工作的。结果是:ji+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

因此,显然分配jtokktom并不意味着值被复制,而是这些名称被绑定在一起。我可以理解。但是如果我删除显示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 的惯用方式,但我想真正了解表达式引擎,我无法解释这种行为。

4

1 回答 1

8

这里有几个相互影响的问题。

首先,您的语句是倒序排列的:您在设置 j 之前设置 k=j,这导致它未定义。

其次,更重要的是,不应使用插值表达式(“{{}}”中的表达式)更改范围的状态。这是有充分理由的:

插值和手表

插值的工作方式是,当它编译你的 html 时,角度会scope.$watch在每个插值表达式上注册 a。

但是这些被监视的表达式可以在摘要期间执行多次:每当侦听器修改作用域时,它会导致 angular 再次运行该作用域上的监视。出于这个原因,被监视的表达式真的应该是“幂等的”:即,它们应该没有副作用/不会导致状态变化。这是来自文档$watch

每次调用 $digest() 时都会调用 watchExpression,并且应该返回将被监视的值。(由于 $digest() 在检测到更改时会重新运行,因此 watchExpression 可以每个 $digest() 执行多次,并且应该是幂等的。)

跟踪示例

具体来说,这就是您的示例中发生的情况。首先,它与中继器无关。每个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>

(这是小提琴

摘要是这样进行的:

  1. 评估{{ i = 42 }}scope.i为 42,并在文档中显示“42”。然而,由于范围发生了变化,还设置了一个“”标志,这意味着我们将再次循环遍历手表(参见下面的步骤 5)
  2. 求值{{ m = k; k = j; j = i+1; i }}scope.mscope.kundefined,和scope.j到 43,并在文档中显示“42”。
  3. 评估{{ j }}在文档中显示“43”。
  4. 评估{{ k }}然后{{ m }}两者都只是在文档中显示“”,因为它们当前未定义。
  5. 现在,由于设置了脏标志,我们重复所有这些监视;这一次,当我们运行时{{ m = k; k = j; j = i+1; i }},k = j 有效,因为 j 存在;所以 k 被赋值为 43。
  6. 到了{{ k }}这个时候,它的值已经从 undefined 变为 2,所以再次设置了脏标志,但现在文档显示“k = 2”。但是,{{ m }}仍然是未定义的。
  7. 就像 5-6 一样,我们再次遍历手表,这一次,{{ m = k; k = j; j = i+1; i }}m 被设置为 43。

Tangent:双处理手表?

切线地,我在跟踪摘要代码时遇到了一件有趣的事情:似乎第一次通过手表时,脏标志总是设置为真,因为从未检查过的手表没有记录的“最后”值。似乎这会导致许多不必要的双重处理。即使观察值是像 {{2}} 这样的常量,这似乎也是正确的。我在这里误解了什么吗?

于 2013-07-12T16:17:17.490 回答