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

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

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(pre 1.0 RC)允许您与$scope方法交替使用,但现在不再这样了。 在范围内定义的方法this$scope是可以互换的(angular将this设置为$scope ),但是在你的控制器构造函数中是不可以的。

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

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

简短的回答

  • this
    • 当控制器构造函数被调用时, this是控制器。
    • $scope对象上定义的函数被调用时, this就是“函数被调用时的作用域”。 这可能(或可能不)是函数定义的$scope 。 所以,在函数内部, this$scope可能一样。
  • $scope
    • 每个控制器都有一个关联的$scope对象。
    • 一个控制器(构造函数)函数负责在关联的$scope上设置模型属性和函数/行为。
    • 只有在这个$scope对象上定义的方法(和父范围对象,如果正在播放原型inheritance)才能从HTML / view中访问。 例如,从ng-click ,filter等

很长的回答

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

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

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

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

在“选项卡”控制器构造函数执行后,我们有以下几点:

在tab控制器构造函数之后

黑色虚线表示原型inheritance – 隔离范围原型inheritance范围 。 (它不会从HTML中遇到指令的地方有效地inheritance。)

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

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

那么红色虚线就是答案。 addPane()函数的“范围”(我在这里指的是JavaScript的函数范围/闭包)给函数的访问隔离$ scope的选项卡。 也就是说,addPane()可以访问上图中的“选项卡IsolateScope”,因为在定义addPane()时创build了一个闭包。 (如果我们改为在选项卡$ 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关联的ParentCtrl

点击第二个链接就会发现this$scope一样的,因为“ 函数调用时的作用域”是与ChildCtrl相关的ChildCtrl 。 所以在这里, this被设置为ChildCtrl$scope 。 在这个方法里, $scope仍然是ParentCtrl的$ scope。

小提琴

我试图不在$ scope中定义的函数中使用this函数,因为这会影响到$ scope,特别是考虑到ng-repeat,ng-include,ng-switch和directives都可以创build自己的子作用域。

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

pane指令确实require: '^tabs' ,它将tab控制器对象从父指令放入链接函数中。

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

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

我只是读了一个关于两者之间差异的非常有趣的解释,并且越来越倾向于将模型附加到控制器,并且别名控制器将模型绑定到视图。 http://toddmotto.com/digging-into-angulars-controller-as-syntax/是文章&#x3002; 他没有提到它,但是当定义指令时,如果你需要在多个指令之间共享一些东西,而不想要一个服务(有合法的情况下服务是一件麻烦事),那么将数据附加到父指令的控制器。 $ scope服务提供了很多有用的东西,$ watch是最明显的,但是如果你只需要将数据绑定到视图,那么在模板中使用plain控制器和'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> 

我build议你阅读下面的post: http : //codetunnel.io/angularjs-controller-as-or-scope/

它很好地描述了使用“Controller as”来暴露“$ scope”variables的好处。

我知道你问的是具体的方法,而不是变数,但我认为最好坚持一种技术,并与之保持一致。

所以我认为,由于后面讨论的variables问题,最好只使用“控制器作为”技术,并将其应用于方法。

先前版本的Angular(pre 1.0 RC)允许您与$ scope方法交替使用,但现在不再这样了。 在范围内定义的方法this和$ scope是可以互换的(angular将它设置为$ scope),但是在你的控制器构造函数中是不可以的。

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

 return angular.extend($scope, this); 

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

这有一个很好的影响,通过控制器对象来访问父范围,你可以在子节点获得require: '^myParentDirective'