提问者:小点点

AngularJS控制器中的“this”与$scope


在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是可互换的(角度将this设置为$scope),但在控制器构造函数中则不可互换。

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


共3个答案

匿名用户

this$scope如何在AngularJS控制器中工作?”

简短的回答:

  • this
    • 调用控制器构造函数时,是控制器。
    • 调用在$scope对象上定义的函数时,this是“调用函数时有效的作用域”。这可能(也可能不会!)是定义函数的$scope。因此,在函数内部,this$scope可能不相同。
    • 每个控制器都有一个关联的$scope对象。
    • 控制器(构造函数)函数负责在其关联的$scope上设置模型属性和函数/行为。
    • 只有在此$scope对象上定义的方法(以及父作用域对象,如果正在使用原型继承)才能从HTML/视图访问。例如,从ng-click,筛选器等

    长答:

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

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

    它是在controller对象上创建的,而不是在$Scope上创建的。视图无法看到addPane函数--它们只能访问在$Scope上定义的函数。换句话说,在HTML中,这是行不通的:

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

    在“tabs”控制器构造函数执行之后,我们有以下内容:

    黑线表示原型继承--隔离作用域典型地从作用域继承。(它并不典型地从HTML中遇到指令的有效作用域继承。)

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

    因此,这就引出了一个问题:如果我们只能访问tabs控制器,那么我们如何访问isolate$scope选项卡(这是我们真正想要的)?

    红色虚线就是答案。addPane()函数的“scope”(这里指的是JavaScript的scope/closures函数)为该函数提供了对isolate$scope选项卡的访问权限。即,addPane()可以访问上面关系图中的“Tabs IsolateScope”,因为在定义addPane()时创建了一个闭包。(如果我们在tabs$scope对象上定义addPane(),则pane指令将不能访问该函数,因此它将无法与tabs$scope通信。)

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

    在$scope上定义的函数中,this被设置为“函数被调用的位置/时间有效的$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>
    

    父Ctrl(仅)具有

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

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

    单击第二个链接将显示thes$scope不相同,因为“调用函数时有效的作用域”是与ChildCtrl相关联的作用域。因此在这里,this被设置为childCtrl$scope。在该方法内部,$scope仍然是ParentCtrl的$scope。

    小提琴

    我尽量不在$scope上定义的函数中使用this,因为会让人混淆哪个$scope受到了影响,特别是考虑到ng-repeat,ng-include,ng-switch和指令都可以创建它们自己的子作用域。

匿名用户

之所以将“add pane”分配给它,是因为指令。

pane指令需要:'^tabs',它将父指令中的tabs控制器对象放入链接函数中。

addpane被分配给this,以便pane链接函数可以看到它。然后在Pane链接函数中,AddPane只是Tabs控制器的一个属性,它只是TabsControllerObject.AddPane。因此pane指令的链接函数可以访问tabs控制器对象,从而访问addPane方法。

我希望我的解释足够清楚。这有点难以解释。

匿名用户

我刚刚读了一篇关于两者之间区别的非常有趣的解释,以及越来越多的人倾向于将模型附加到控制器上,并对控制器进行别名以将模型绑定到视图。http://toddmotto.com/digging-into-angulars-controller-as-syntax/是这篇文章。
他没有提到这一点,但是在定义指令时,如果您需要在多个指令之间共享一些东西,并且不想要服务(在服务是一个麻烦的合法情况下),那么就将数据附加到父指令的控制器上。

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