协慌网

登录 贡献 社区

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 ,则代码会中断。

文档说实际上存在差异,但没有提到差异是什么:

角(预 1.0 RC)的早期版本允许你使用this与互换$scope的方法,但是这不再是这种情况。里面对范围定义的方法this$scope是可以互换(角套this$scope ),而不是其他你的控制器构造函数中。

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

答案

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

简短回答

  • this
    • 调用控制器构造函数时, this是控制器。
    • 当调用$scope对象上定义的函数时, this是 “调用函数时生效的范围”。这可能(或可能不是!)是定义函数的$scope 。因此,在函数内部, this$scope可能一样。
  • $scope
    • 每个控制器都有一个关联的$scope对象。
    • 控制器(构造函数)函数负责在其关联的$scope上设置模型属性和函数 / 行为。
    • 只能在 HTML / 视图中访问在此$scope对象上定义的方法(以及父范围对象,如果是原型继承)。例如,来自ng-click ,过滤器等。

答案很长

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

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

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

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

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

在tabs控制器构造函数之后

黑色虚线表示原型继承 - 原型继承自Scope的隔离范围。 (它没有原型地继承 HTML 中遇到指令的有效范围。)

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

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

嗯,红色虚线就是答案。 addPane()函数的 “范围”(我指的是 JavaScript 的函数范围 / 闭包)给函数访问选项卡隔离 $ scope。即,addPane()可以访问上图中的 “tabs IsolateScope”,因为在定义 addPane()时创建了一个闭包。 (如果我们在选项卡 $ scope 对象上定义了 addPane(),则窗格指令将无法访问此函数,因此它无法与选项卡 $ scope 通信。)

要回答你问题的另一部分: how does $scope work in controllers?

在 $ 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>

并且ParentCtrlParentCtrl )有

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

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

单击第二个链接将显示this并且$scope 相同,因为 “ 调用函数时生效的范围” 是与ChildCtrl关联的范围。所以在这里, this被设置为ChildCtrl$scope 。在方法内部, $scope仍然是ParentCtrl的 $ scope。

小提琴

我尽量不使用this上 $ 范围内定义的函数中,因为它变得扑朔迷离其中 $ 范围受到影响,尤其是考虑到 NG - 重复,NG - 包括 NG - 开关和指令都可以创建自己的子作用域。

“addPane” 分配给它的原因是因为<pane>指令。

pane指令确实require: '^tabs' ,它将来自父指令的 tabs 控制器对象放入 link 函数中。

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

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

我刚刚阅读了关于两者之间差异的一个非常有趣的解释,并且越来越倾向于将模型附加到控制器并且将控制器别名为控制器以将模型绑定到视图。 http://toddmotto.com/digging-into-angulars-controller-as-syntax/是这篇文章。他没有提到它,但是在定义指令时,如果你需要在多个指令之间共享某些内容并且不需要服务(存在服务麻烦的合法情况),那么将数据附加到父指令的控制器。 $ scope 服务提供了大量有用的东西,$ watch 是最明显的,但如果你只需要将数据绑定到视图,那么在模板中使用普通控制器和'controller as' 是可以的,并且可以说是更好的选择。