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>
在“选项卡”控制器构造函数执行后,我们有以下几点:
黑色虚线表示原型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>
和ParentCtrl
( ParentCtrl
)
$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/是文章。; 他没有提到它,但是当定义指令时,如果你需要在多个指令之间共享一些东西,而不想要一个服务(有合法的情况下服务是一件麻烦事),那么将数据附加到父指令的控制器。 $ 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'