在AngularJS主页的“创建组件”部分,有这样一个例子:

controller: function($scope, $element) {
  var panes = $scope.panes = [];
  $scope.select = function(pane) {
    angular.forEach(panes, function(pane) {
      pane.selected = false;
    });
    pane.selected = true;
  }
  this.addPane = function(pane) {
    if (panes.length == 0) $scope.select(pane);
    panes.push(pane);
  }
}

注意select方法是如何添加到$scope的,而addPane方法是添加到this的。如果我把它改成$scope。addPane,代码崩溃。

文档说实际上是有区别的,但它没有提到区别是什么:

以前的Angular版本(1.0 RC之前)允许你将此方法与$scope方法互换使用,但现在不再是这样了。在作用域上定义的方法内部this和$scope是可以互换的(angular将this设置为$scope),但在控制器构造函数内部则不能互换。

this和$scope在AngularJS控制器中是如何工作的?


当前回答

“this和$scope在AngularJS控制器中是如何工作的?”

简短的回答:

this When the controller constructor function is called, this is the controller. When a function defined on a $scope object is called, this is the "scope in effect when the function was called". This may (or may not!) be the $scope that the function is defined on. So, inside the function, this and $scope may not be the same. $scope Every controller has an associated $scope object. A controller (constructor) function is responsible for setting model properties and functions/behaviour on its associated $scope. Only methods defined on this $scope object (and parent scope objects, if prototypical inheritance is in play) are accessible from the HTML/view. E.g., from ng-click, filters, etc.

长一点的回答:

控制器函数是JavaScript的构造函数。当构造函数执行时(例如,当视图加载时),this(即“函数上下文”)被设置为控制器对象。在"tabs"控制器构造函数中,当addPane函数被创建时

this.addPane = function(pane) { ... }

它创建在控制器对象上,而不是在$scope上。视图不能看到addPane函数——它们只能访问在$scope上定义的函数。换句话说,在HTML中,这是行不通的:

<a ng-click="addPane(newPane)">won't work</a>

在"tabs"控制器构造函数执行后,我们得到如下结果:

虚线表示原型继承——一个孤立的范围原型继承自scope。(它并不从HTML中遇到指令的作用域继承。)

现在,pane指令的link函数想要与tabs指令通信(这实际上意味着它需要以某种方式影响tabs隔离$scope)。可以使用事件,但另一种机制是让窗格指令需要选项卡控制器。(似乎没有机制让窗格指令要求选项卡$scope。)

所以,这就引出了一个问题:如果我们只能访问选项卡控制器,我们如何访问选项卡隔离的$scope(这是我们真正想要的)?

Well, the red dotted line is the answer. The addPane() function's "scope" (I'm referring to JavaScript's function scope/closures here) gives the function access to the tabs isolate $scope. I.e., addPane() has access to the "tabs IsolateScope" in the diagram above because of a closure that was created when addPane() was defined. (If we instead defined addPane() on the tabs $scope object, the pane directive would not have access to this function, and hence it would have no way to communicate with the tabs $scope.)

回答你问题的另一部分:$scope如何在控制器中工作?:

在$scope上定义的函数中,这将被设置为“在函数被调用的位置/时间生效的$scope”。假设我们有以下HTML:

<div ng-controller="ParentCtrl">
   <a ng-click="logThisAndScope()">log "this" and $scope</a> - parent scope
   <div ng-controller="ChildCtrl">
      <a ng-click="logThisAndScope()">log "this" and $scope</a> - child scope
   </div>
</div>

而ParentCtrl (only)具有

$scope.logThisAndScope = function() {
    console.log(this, $scope)
}

单击第一个链接将显示this和$scope是相同的,因为“函数被调用时生效的作用域”是与ParentCtrl关联的作用域。

点击第二个链接将显示this和$scope是不一样的,因为“函数被调用时生效的作用域”是与ChildCtrl关联的作用域。这里,这个被设置为ChildCtrl的$scope。在方法内部,$scope仍然是ParentCtrl的$scope。

小提琴

我尽量不在$scope上定义的函数中使用这个,因为这会让人感到困惑,尤其是考虑到ng-repeat、ng-include、ng-switch和指令都可以创建自己的子作用域。

其他回答

我建议你阅读下面的帖子: "Controller as"还是"$scope"?

它很好地描述了使用“Controller as”公开变量而不是“$scope”的优点。

我知道你特别问了关于方法而不是变量的问题,但我认为最好坚持一种技术并与之保持一致。

所以在我看来,由于文章中讨论的变量问题,最好只使用“Controller as”技术,并将其应用于方法。

在本课程(https://www.codeschool.com/courses/shaping-up-with-angular-js)中,他们解释了如何使用“this”和许多其他东西。

如果你通过"this"方法向控制器添加方法,你必须在视图中用控制器名"dot"你的属性或方法来调用它。

例如,在视图中使用控制器,你可能会有这样的代码:

    <div data-ng-controller="YourController as aliasOfYourController">

       Your first pane is {{aliasOfYourController.panes[0]}}

    </div>

我刚刚读了一篇关于两者区别的非常有趣的解释,而且越来越多的人倾向于将模型附加到控制器上,并通过别名将控制器绑定到视图上。http://toddmotto.com/digging-into-angulars-controller-as-syntax/是文章。

注意:原始链接仍然存在,但格式的变化使其难以阅读。在原版中更容易看到。

他没有提到,但是在定义指令时,如果你需要在多个指令之间共享一些东西,并且不想要一个服务(在某些情况下,服务是一个麻烦),那么将数据附加到父指令的控制器上。

$scope服务提供了很多有用的东西,$watch是最明显的,但如果您需要将数据绑定到视图,那么在模板中使用普通控制器和“controller as”是很好的,而且可以说是更可取的。

以前的Angular版本(1.0 RC之前)允许你使用这个 可以与$scope方法互换,但这不再是 的情况。在作用域this和$scope上定义的方法内部 可互换(angular将其设置为$scope),否则就不行 在你的控制器构造函数中。

要恢复这种行为(有人知道为什么改变吗?)你可以添加:

return angular.extend($scope, this);

在控制器函数的末尾(假设$scope被注入到这个控制器函数中)。

这有一个很好的效果,可以通过控制器对象访问父作用域,你可以在child中使用require: '^myParentDirective'

addPane被赋值给它的原因是<pane>指令。

窗格指令确实需要:'^tabs',它将来自父指令的tabs控制器对象放到link函数中。

addPane被赋值给它,以便窗格链接函数可以看到它。然后在窗格链接函数中,addPane只是选项卡控制器的一个属性,它就是tabscontrollerobject。addPane。因此,pane指令的链接函数可以访问tabs控制器对象,从而访问addPane方法。

我希望我的解释足够清楚。这很难解释。