简体   繁体   English

为什么我在Angular JS中获得两个方法调用?

[英]Why am I getting two method calls in Angular JS?

I have the following example: 我有以下示例:

 var app = angular.module('myApp', []); app.controller('myCtrl', function($scope) { $scope.testMethod = function() { alert('hi'); } }); 
 <!DOCTYPE html> <html> <head> <script data-require="angular.js@1.5.0" data-semver="1.5.0" src="//cdnjs.cloudflare.com/ajax/libs/angular.js/1.5.0/angular.min.js"></script> </head> <body ng-app="myApp"> <div ng-controller="myCtrl"> {{testMethod()}} </div> </body> </html> 

I have called the method only once in view template. 我在视图模板中只调用了一次该方法。 But why is it executed twice? 但为什么要执行两次呢?

When you use functions in binding expressions Angular will reevaluate the expression on every $digest phase. 当您在绑定表达式中使用函数时,Angular将在每个$ digest阶段重新评估表达式。 The reason behind this is that functions can return a response but there is no way for Angular to know if the result won't change in the next function call. 这背后的原因是函数可以返回响应,但是Angular无法知道结果在下一个函数调用中是否会发生变化。

Angular ensures that it will show the latest correct up to date value in the only way possible in this case - by calling the function every time there is a change in the scope. Angular确保在这种情况下以唯一可能的方式显示最新的正确最新值 - 通过每次范围发生变化时调用函数。

You'll see people calling this "$digest phase". 你会看到人们称之为“$ digest阶段”。 It can occur due to many reasons. 它可能由于许多原因而发生。 For the sake of this explanation I'm simplifying stuff. 为了这个解释,我简化了一些东西。 If you want to know more read https://docs.angularjs.org/guide/scope 如果您想了解更多信息,请阅读https://docs.angularjs.org/guide/scope

As a general rule - avoid binding to a function. 作为一般规则 - 避免绑定到函数。 Instead remember the function response in a $scope variable and bind to it instead. 而是记住$scope变量中的函数响应并改为绑定它。 Binding many times to a function might lead to performance issues when bindings count grows in your project. 在项目中绑定计数增加时,多次绑定到函数可能会导致性能问题。

EDIT - Answer to sahbeewah comments (see below) 编辑 - 回答sahbeewah评论(见下文)

Let's change the post example. 让我们改变帖子的例子。 Open the console and run the code below. 打开控制台并运行下面的代码。

 var app = angular.module('myApp', []); app.controller('myCtrl', function($scope) { var i = 0; $scope.testMethod = function() { alert('hi'); i++; return i; // Return a different value every time } }); 
 <!DOCTYPE html> <html> <head> <script data-require="angular.js@1.5.0" data-semver="1.5.0" src="//cdnjs.cloudflare.com/ajax/libs/angular.js/1.5.0/angular.js"></script> </head> <body ng-app="myApp"> <div ng-controller="myCtrl"> {{testMethod()}} </div> </body> </html> 

For the example purposes it's important for $scope.testMethod to return different value every time it's called. 出于示例目的, $scope.testMethod每次调用时返回不同的值都很重要。

You'll notice the following: 你会注意到以下几点:

  • "Hi" alert will be shown multiple times “嗨”警报将多次显示
  • After you see it multiple times the following error will appear: 多次看到后会出现以下错误:

    Error: [$rootScope:infdig] 10 $digest() iterations reached. 错误:[$ rootScope:infdig] 10 $ digest()迭代达成。 Aborting! 中止!

So, what happened? 所以发生了什么事? Every time we change the $scope state here angular runs another digest phase until it "stabilizes" ( there are no more changes ). 每当我们改变$scope状态时,angular会运行另一个摘要阶段,直到它“稳定”( 没有更多的变化 )。 There is a limit on 10 consecutive $digest phases. 连续10个$ digest阶段有一个限制。

In the thread post angular calls $digest when the controller is attached and then, because we change the $scope, it calls one more digest. 在附加控制器的线程调用角度调用$ digest然后,因为我们更改$ scope,它再调用一个摘要。 If we remove all the bindings from the code only one digest will occur. 如果我们从代码中删除所有绑定,则只会发生一个摘要。

We can easily check this. 我们可以轻松检查这一点。 Remove the {{testMethod()}} line from the example and then place a breakpoints in angular code: $digest, line 16700 ( angular 1.5.0 ) . 从示例中删除{{testMethod()}}行,然后在角度代码中放置一个断点: $ digest,第16700行( 角度1.5.0
On this line you'll see if ((dirty || asyncQueue.length) && !(ttl--)) { . 在这一行你会看到if ((dirty || asyncQueue.length) && !(ttl--)) {

Your breakpoint will now be hit only once. 您的断点现在只会被击中一次。 Let's look 2 lines above. 我们看看上面两行。 Angular team wrote an interesting comment there: Angular团队在那里写了一篇有趣的评论:

// `break traverseScopesLoop;` takes us to here

It's a self explanatory comment and if we go up to line 16629 we'll see 这是一个自我解释的评论,如果我们走到16629行,我们会看到

do { // "while dirty" loop

There is a variable in the code named "dirty" that creates a do...while loop. 名为“dirty”的代码中有一个变量,它创建了一个do...while循环。 While the scope is dirty ( there are changes detected ) you'll stay in this loop. 虽然范围很脏检测到更改 ),但您将保持此循环。 A change is any change on any scope variable used in any way by any kind of binding ( $watch ). 更改是任何类型绑定以任何方式使用的任何范围变量的任何更改( $ watch )。

That's why we have 2 $digest phases here. 这就是我们在这里有2个$ digest阶段的原因。

You can find Angular 1.5 here . 你可以在这里找到Angular 1.5。 For debugging purposes I've switched to the non-minified versions. 出于调试目的,我已切换到非缩小版本。

avladov's answer is correct, but perhaps doesn't cover what sahbeewah asked. avladov的回答是正确的,但也许并未涵盖sahbeewah所要求的内容。 The update on the answer is explanatory, but honestly the answer boils down to much simpler point. 答案的更新是解释性的,但老实说答案归结为更简单的一点。

  • Angular pairs all watchers with special property (called last in v1.4.10). Angular将所有具有特殊属性的观察者配对(在v1.4.10中称为last )。

  • The initial value of last is function initWatchVal() which is a private value in angular and thus you cannot return without some serious hacks. 的初始值last功能initWatchVal(),它是在角私人值,因此你不能没有一些严重的黑客回来。

  • Dirty checking works by evaluating the expression and comparing to previous evaluation (the value of last ). 脏检查的工作原理是评估表达式并与之前的评估( last的值)进行比较。

  • If these values don't match, new round of evaluations is scheduled. 如果这些值不匹配,则安排新一轮评估。

The point being: as you can't return the initial value, new round of evaluations is always scheduled after the first evaluation of new watchers. 关键在于:由于您无法返回初始值,因此在第一次评估新观察者之后, 始终会安排新一轮评估。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM