AngularJS控制器中的'this'与$ scope

Ale*_*ine 1013 this angularjs angularjs-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);
  }
}
Run Code Online (Sandbox Code Playgroud)

注意如何select添加方法$scope,但是addPane添加了方法this.如果我将其更改为$scope.addPane,则代码会中断.

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

以前版本的Angular(pre 1.0 RC)允许您this与该$scope方法互换使用,但现在不再是这种情况了.内的方法上的范围限定this并且$scope是可互换的(角套this$scope),但是不另外你的控制器构造内部.

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

Mar*_*cok 989

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

简短回答:

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

答案很长:

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

this.addPane = function(pane) { ... }
Run Code Online (Sandbox Code Playgroud)

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

<a ng-click="addPane(newPane)">won't work</a>
Run Code Online (Sandbox Code Playgroud)

执行"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>
Run Code Online (Sandbox Code Playgroud)

而且ParentCtrl(Solely)有

$scope.logThisAndScope = function() {
    console.log(this, $scope)
}
Run Code Online (Sandbox Code Playgroud)

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

点击第二个链接将揭示this$scope一样的,因为" 有效范围时调用函数 "是与相关的范围ChildCtrl.所以在这里,this设置为ChildCtrl's $scope.在方法内部,$scope仍然ParentCtrl是$ $范围.

小提琴

我尝试不在this$ scope中定义的函数内部使用,因为令人困惑的是哪个$ scope受到影响,特别是考虑到ng-repeat,ng-include,ng-switch和指令都可以创建自己的子作用域.

  • 固有的复杂性太多了. (81认同)
  • 注意,现在可以通过命名控制器直接在模板中调用addPane()函数:"MyController as myctrl",然后是myctrl.addPane().见http://docs.angularjs.org/guide/concepts#controller (77认同)
  • 这是一个非常有用的答案,但当我回到实际问题时([如何在使用'this'定义的控制器方法中调用$ scope.$ apply()](http://stackoverflow.com/questions/27490197/what-is-the-this-equivalent-scope-apply-in-angular-js))我无法解决它.因此,虽然这仍然是一个有用的答案,但我发现"固有的复杂性"令人费解. (11认同)
  • Javascript - 很多绳索[挂起自己]. (10认同)
  • @tamakisquare,我相信你引用的粗体文本适用于调用控制器构造函数时 - 即,当创建控制器=与$ scope关联时.当任意JavaScript代码调用$ scope对象上定义的方法时,它不适用于以后. (6认同)
  • 所以你应该避免使用`this`而只是使用范围?还是我错过了什么? (6认同)
  • @ user2499325,函数`logThisAndScope`创建一个闭包,变量`$ scope`(与ParentCtrl关联的变量)是该闭包的一部分.当单击第二个链接时,在ChildCtrl的`$ scope`上没有定义`logThisAndScope`函数,因此原型继承启动并且检查父`$ scope`(与ParentCtrl相关联的那个).在`$ scope`上找到`logThisAndScope`函数并执行它.当该函数记录`$ scope`时,它是与闭包相关联的`$ scope`,因此与ParentCtrl相关联.我希望有所帮助. (2认同)
  • 我也发现这个https://toddmotto.com/digging-into-angulars-controller-as-syntax/有帮助 (2认同)

And*_*lin 55

"addPane"分配给它的原因是因为该<pane>指令.

pane指令require: '^tabs'将来自父指令的tabs控制器对象放入链接函数中.

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

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

  • 感谢您的解释.文档似乎控制器只是一个设置范围的函数.如果在范围内发生所有操作,为什么控制器会被视为对象?为什么不将父范围传递给链接函数?编辑:为了更好地表达这个问题,如果控制器方法和范围方法都在相同的数据结构(范围)上运行,为什么不把它们全部放在一个地方? (3认同)
  • 谢谢@Andy的小提琴.在你的小提琴中,指令不是创建一个新的作用域,所以我可以看到链接函数如何直接访问控制器的作用域(因为只有一个作用域).选项卡和窗格指令使用隔离范围(即,创建的新子范围不是从父范围原型继承的).对于隔离范围的情况,似乎在控制器上定义一个方法(使用'this')是允许另一个指令获得(间接)访问另一个(隔离)范围的唯一方法. (3认同)

Der*_*rek 27

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


Lir*_*mer 19

我建议你阅读以下帖子: AngularJS:"Controller as"或"$ scope"?

它很好地描述了使用"Controller as"在"$ scope"上公开变量的优点.

我知道你具体问过方法而不是变量,但我认为最好坚持使用一种技术并与之保持一致.

所以我认为,由于帖子中讨论的变量问题,最好只使用"Controller as"技术并将其应用于方法.


小智 15

在本课程(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>
Run Code Online (Sandbox Code Playgroud)

  • 那个课程根本没有提到$ scope,他们只是使用`as`和`this`所以它怎么能帮助解释这个区别呢? (16认同)
  • 我对Angular的第一次接触来自于上面提到的过程,并且由于`$ scope`从未被引用过,我学会了在控制器中使用`this`.问题是,当你开始在你的控制器中处理promises时,你对`this`有很多引用问题,并且必须开始做`var me = this`这样的事情来引用`this`中的模型.承诺返回功能.因此,我仍然很困惑我应该使用哪种方法,`$ scope`或`this`. (10认同)
  • 完成课程后,我立即被使用`$ scope`的代码搞糊涂了,所以谢谢你提到它. (6认同)
  • 重要的是要知道 `ng-controller="MyCtrl as MC"` 相当于将 `$scope.MC = this` 放在控制器本身中 - 它在作用域上定义了 MyCtrl 的实例(this)以供使用通过 `{{ MC.foo }}` 在模板中 (2认同)