'this' vs $scope in AngularJS controllers
在AngularJS主页的"创建组件"部分,有以下示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 | 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); } } |
注意如何将
文件上说事实上有区别,但没有提到区别是什么:
Previous versions of Angular (pre 1.0 RC) allowed you to use
this interchangeably with the$scope method, but this is no longer the case. Inside of methods defined on the scopethis and$scope are interchangeable (angular setsthis to$scope ), but not otherwise inside your controller constructor.
在AngularJS控制器中,
"How does
this and$scope work in AngularJS controllers?"
简短回答:
this - 当调用控制器构造函数函数时,
this 是控制器。 - 当调用在
$scope 对象上定义的函数时,this 是"调用函数时有效的范围"。这可能(也可能不会!)是定义函数的$scope 。因此,在函数内部,this 和$scope 可能不同。
- 当调用控制器构造函数函数时,
$scope - 每个控制器都有一个关联的
$scope 对象。 - 控制器(构造函数)函数负责在其关联的
$scope 上设置模型属性和函数/行为。 - 只有在这个
$scope 对象上定义的方法(以及父作用域对象,如果原型继承正在运行)才可以从HTML/视图访问。例如,来自ng-click 、过滤器等。
- 每个控制器都有一个关联的
长回答:
控制器函数是一个javascript构造函数函数。当构造器函数执行时(例如,当视图加载时),
1 | this.addPane = function(pane) { ... } |
它是在控制器对象上创建的,而不是在$scope上。视图不能看到addpane函数——它们只能访问在$scope上定义的函数。换句话说,在HTML中,这不起作用:
1 | won't work |
在"tabs"控制器构造函数函数执行之后,我们有以下内容:
虚线黑线表示原型继承——一个孤立的范围原型继承自范围。(它不是从HTML中遇到指令的有效作用域原型继承的。)
现在,pane指令的link函数希望与tabs指令通信(这实际上意味着它需要以某种方式影响tabs隔离$scope)。可以使用事件,但另一种机制是让pane指令
因此,这就引出了一个问题:如果我们只能访问Tabs控制器,那么如何访问Tabs隔离$scope(这是我们真正想要的)?
答案是红色虚线。addpane()函数的"scope"(这里我指的是javascript的函数作用域/闭包)提供了对隔离$scope的选项卡的函数访问。也就是说,addpane()可以访问上图中的"tabs isolatescope",因为在定义addpane()时创建了一个闭包。(如果我们改为在tabs$scope对象上定义addpane(),pane指令将无法访问此函数,因此它将无法与tabs$scope通信。)
回答问题的另一部分:
在$scope上定义的函数中,
1 2 3 | log"this" and $scope - parent scope log"this" and $scope - child scope |
而
1 2 3 | $scope.logThisAndScope = function() { console.log(this, $scope) } |
单击第一个链接将显示
单击第二个链接将显示
小提琴
我尽量不要在$scope上定义的函数内部使用
之所以将'addpane'分配给它,是因为
我希望我的解释足够清楚。这很难解释。
我刚刚读了一个非常有趣的关于这两者之间区别的解释,以及越来越多的偏好将模型附加到控制器上,并将控制器别名为将模型绑定到视图上。本文是http://toddmotto.com/digging-into-angulars-controller-as-syntax/。他没有提到这一点,但是在定义指令时,如果您需要在多个指令之间共享某些内容,并且不需要服务(在合法情况下,服务很麻烦),那么将数据附加到父指令的控制器上。$scope服务提供了很多有用的东西,$watch是最明显的,但是如果您只需要将数据绑定到视图,那么在模板中使用plain控制器和"controller as"就可以了,而且可以说是更好的选择。
我建议你阅读以下文章:AngularJS:"控制器为"或"$scope"?
它很好地描述了使用"controller as"在"$scope"上公开变量的优点。
我知道你特别询问了方法而不是变量,但我认为最好坚持一种技术并与之保持一致。
因此,在我看来,由于本文讨论的变量问题,最好使用"控制器作为"技术,并将其应用到方法中。
在本课程(https://www.codeschool.com/courses/shaping-up-with-angular-js)中,他们解释了如何使用"this"和其他许多东西。
如果通过"this"方法向控制器添加方法,则必须在视图中用控制器的名称"dot"调用属性或方法。
例如,在视图中使用控制器时,可能会有如下代码:
1 | Your first pane is {{aliasOfYourController.panes[0]}} |
Previous versions of Angular (pre 1.0 RC) allowed you to use this
interchangeably with the $scope method, but this is no longer the
case. Inside of methods defined on the scope this and $scope are
interchangeable (angular sets this to $scope), but not otherwise
inside your controller constructor.
恢复这种行为(有人知道为什么会改变吗?)您可以添加:
1 | return angular.extend($scope, this); |
在控制器功能结束时(前提是$scope已注入到该控制器功能中)。
这有一个很好的效果,即通过控制器对象访问父范围,您可以使用
$scope有一个不同的"this",然后是控制器"this"。因此,如果您将console.log(this)放在控制器中,它将为您提供一个对象(controller),而这个.addpane()将addpane方法添加到控制器对象中。但是$scope有不同的作用域,其作用域中的所有方法都需要被$scope.methodname()接受。控制器内的
1 2 3 4 | $scope.functionName(){ this.name="Name"; //or $scope.myname="myname"//are same} |
将此代码粘贴到编辑器中并打开控制台以查看…
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 | <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta http-equiv="X-UA-Compatible" content="ie=edge"> this $sope vs controller <script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.6.7/angular.min.js"> var app=angular.module("myApp",[]); app.controller("ctrlExample",function($scope){ console.log("ctrl 'this'",this); //this(object) of controller different then $scope $scope.firstName="Andy"; $scope.lastName="Bot"; this.nickName="ABot"; this.controllerMethod=function(){ console.log("controllerMethod",this); } $scope.show=function(){ console.log("$scope 'this",this); //this of $scope $scope.message="Welcome User"; } }); </head> <body ng-app="myApp"> Comming From $SCOPE :{{firstName}} Comming from $SCOPE:{{lastName}} Should Come From Controller:{{nickName}} <p> Blank nickName is because nickName is attached to 'this' of controller. </p> <button ng-click="controllerMethod()">Controller Method</button> <button ng-click="show()">Show</button> <p> {{message}} </p> </body> </html> |