在angularjs里 ‘this’ 和 $scope

'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);
  }
}

注意如何将select方法添加到$scope中,而将addPane方法添加到this中。如果我把它改成$scope.addPane,代码就会中断。

文件上说事实上有区别,但没有提到区别是什么:

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.

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


"How does this and $scope work in AngularJS controllers?"

简短回答:

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

长回答:

控制器函数是一个javascript构造函数函数。当构造器函数执行时(例如,当视图加载时),this(即"函数上下文")被设置为控制器对象。因此,在"tabs"控制器构造函数函数中,当创建addpane函数时

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

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

1
won't work

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

after tabs controller constructor function

虚线黑线表示原型继承——一个孤立的范围原型继承自范围。(它不是从HTML中遇到指令的有效作用域原型继承的。)

现在,pane指令的link函数希望与tabs指令通信(这实际上意味着它需要以某种方式影响tabs隔离$scope)。可以使用事件,但另一种机制是让pane指令require成为tabs控制器。(pane指令对require的tabs$scope似乎没有任何机制。)

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

答案是红色虚线。addpane()函数的"scope"(这里我指的是javascript的函数作用域/闭包)提供了对隔离$scope的选项卡的函数访问。也就是说,addpane()可以访问上图中的"tabs isolatescope",因为在定义addpane()时创建了一个闭包。(如果我们改为在tabs$scope对象上定义addpane(),pane指令将无法访问此函数,因此它将无法与tabs$scope通信。)

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

在$scope上定义的函数中,this被设置为"调用函数时生效的$scope"。假设我们有以下HTML:

1
2
3
   log"this" and $scope - parent scope
   
      log"this" and $scope - child scope

ParentCtrl(单独)已经

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

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

单击第二个链接将显示this$scope不同,因为"调用函数时生效的作用域"是与ChildCtrl关联的作用域。在这里,this被设置为ChildCtrl$scope。在方法中,$scope仍然是ParentCtrl的$范围。

小提琴

我尽量不要在$scope上定义的函数内部使用this,因为它会混淆哪些$scope会受到影响,特别是考虑到ng repeat、ng include、ng switch和指令都可以创建自己的子范围。


之所以将'addpane'分配给它,是因为指令。

pane指令执行require: '^tabs',它将父指令中的tabs控制器对象放入链接函数中。

addPane被分配给this以便pane链接功能可以看到它。然后在pane链接函数中,addPane只是tabs控制器的一个属性,它只是tabsControllerObject.addPane。因此pane指令的链接函数可以访问tabs控制器对象,从而访问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已注入到该控制器功能中)。

这有一个很好的效果,即通过控制器对象访问父范围,您可以使用require: '^myParentDirective'进入子范围。


$scope有一个不同的"this",然后是控制器"this"。因此,如果您将console.log(this)放在控制器中,它将为您提供一个对象(controller),而这个.addpane()将addpane方法添加到控制器对象中。但是$scope有不同的作用域,其作用域中的所有方法都需要被$scope.methodname()接受。控制器内的this.methodName()表示在控制器对象内添加方法,$scope.functionName()为HTML格式,内部为

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>