AngularJS 控制器中的“this”与 $scope

'this' vs $scope in AngularJS controllers

提问人:Alexei Boronine 提问时间:7/23/2012 最后编辑:Peter MortensenAlexei Boronine 更新时间:11/20/2020 访问量:309632

问:

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

请注意该方法是如何添加到 的,但该方法是如何添加到 的。如果我将其更改为 ,则代码会中断。select$scopeaddPanethis$scope.addPane

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

以前版本的 Angular(1.0 RC 之前)允许您与该方法互换使用,但现在情况已不再如此。在作用域上定义的方法内部,并且可以互换(角度设置为 ),但在控制器构造函数中则不行。this$scopethis$scopethis$scope

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

angularjs angularjs-scope 这个

评论

1赞 Mark Rajcok 9/13/2012
我也觉得这很令人困惑。当视图指定控制器(例如,ng-controller='...')时,与该控制器关联的$scope似乎随之出现,因为视图可以访问$scope属性。但是,当一个指令“需要另一个控制器”(然后在其链接功能中使用它)时,与该另一个控制器关联的$scope不会随之出现?
0赞 Dmitri Zaitsev 12/7/2015
关于“以前的版本......”的令人困惑的报价吗?现在已经删除了吗?那么也许更新会到位?
0赞 abentan 2/1/2018
对于单元测试,如果使用“this”而不是“$scope”,则无法向控制器注入模拟作用域,因此无法进行单元测试。我不认为使用“this”是一个好习惯。

答:

57赞 Andrew Joslin 7/23/2012 #1

将“addPane”分配给此的原因是该指令。<pane>

该指令将父指令中的选项卡控制器对象放入 link 函数中。panerequire: '^tabs'

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

我希望我的解释足够清楚。这有点难以解释。

评论

3赞 Alexei Boronine 7/24/2012
谢谢你的解释。这些文档使控制器看起来只是一个设置范围的函数。如果所有操作都发生在作用域中,为什么控制器会被视为对象?为什么不直接将父作用域传递到链接函数中呢?编辑:为了更好地表达这个问题,如果控制器方法和作用域方法都对相同的数据结构(作用域)进行操作,为什么不将它们全部放在一个地方呢?
0赞 Mark Rajcok 9/13/2012
似乎父作用域没有传递到 lnk func 中,因为希望支持“可重用的组件,这些组件不应该意外读取或修改父作用域中的数据”。但是,如果指令确实想要/需要读取或修改父作用域中的某些特定数据(就像“pane”指令一样),则需要一些努力:“需要”所需父作用域所在的控制器,然后在该控制器上定义一个方法(使用“this”而不是$scope)来访问特定数据。由于所需的父范围没有注入到 lnk func 中,我想这是唯一的方法。
1赞 Andrew Joslin 9/13/2012
嘿,标记,修改指令的范围实际上更容易。您可以只使用链接功能 jsfiddle.net/TuNyj
3赞 Mark Rajcok 1/2/2013
谢谢@Andy的小提琴。在您的小提琴中,该指令没有创建新作用域,因此我可以看到链接函数如何在此处直接访问控制器的作用域(因为只有一个作用域)。tabs 和 pane 指令使用隔离作用域(即,创建新的子作用域,这些子作用域通常不会从父作用域继承)。对于隔离作用域的情况,似乎在控制器上定义方法(使用“this”)是允许另一个指令获得对另一个(隔离)作用域的(间接)访问的唯一方法。
1014赞 Mark Rajcok 1/5/2013 #2

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

简短的回答

  • this
    • 调用控制器构造函数时,是控制器。this
    • 当调用在对象上定义的函数时,是“调用函数时生效的范围”。这可能是(也可能不是)定义函数的依据。所以,在函数内部,可能不一样。$scopethis$scopethis$scope
  • $scope
    • 每个控制器都有一个关联的对象。$scope
    • 控制器(构造函数)函数负责在其关联的 .$scope
    • 只有在此对象(以及父范围对象,如果原型继承起作用)上定义的方法才能从 HTML/视图访问。例如,from 、filters 等。$scopeng-click

长答案

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

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

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

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

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

after tabs controller constructor function

黑色虚线表示原型继承 -- 隔离范围原型继承自 Scope。(它不是从在 HTML 中遇到指令的有效作用域继承的。

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

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

好吧,红色虚线就是答案。addPane() 函数的“scope”(我在这里指的是 JavaScript 的函数 scope/closures)使函数能够访问隔离$scope的选项卡。即,addPane() 可以访问上图中的“选项卡 IsolateScope”,因为在定义 addPane() 时创建了一个闭包。(如果我们在 tabs $scope对象上定义了 addPane(),则 pane 指令将无法访问此函数,因此它将无法与选项卡$scope通信。

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

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

<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>

而(Solely)有ParentCtrl

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

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

单击第二个链接将显示它们,并且它们并不相同,因为“调用函数时生效的作用域”是与 关联的作用域。所以这里,设置为 的 .方法内部,仍然是 的$scope。this$scopeChildCtrlthisChildCtrl$scope$scopeParentCtrl

小提琴

我尽量不要在 $scope 上定义的函数内部使用,因为哪个$scope受到影响会变得令人困惑,特别是考虑到 ng-repeat、ng-include、ng-switch 和指令都可以创建自己的子作用域。this

评论

6赞 Mark Rajcok 2/20/2013
@tamakisquare,我相信您引用的粗体文本适用于调用控制器构造函数时——即创建控制器时 = 与$scope相关联。以后,当任意 JavaScript 代码调用在 $scope 对象上定义的方法时,它就不适用了。
79赞 Christophe Augier 11/30/2013
请注意,现在可以直接在模板中调用 addPane() 函数,方法是将控制器命名为:“MyController as myctrl”,然后命名为 myctrl.addPane()。查看 docs.angularjs.org/guide/concepts#controller
85赞 Inanc Gumus 6/7/2014
固有的复杂性太多。
11赞 dumbledad 12/16/2014
这是一个非常翔实的答案,但是当我回来遇到一个实际问题(如何在使用“this”定义的控制器方法中调用 $scope.$apply()))时,我无法解决。因此,虽然这仍然是一个有用的答案,但我发现“内在的复杂性”令人困惑。
12赞 AlikElzin-kilaka 6/1/2015
Javascript - 很多绳子[吊死自己]。
3赞 Kamil Szot 6/20/2014 #3

以前版本的 Angular(1.0 RC 之前)允许你使用它 可与 $scope 方法互换,但这不再是 箱。在作用域上定义的方法内部,this 和 $scope 是 可互换(angular 将其设置为 $scope),但不能 在控制器构造函数中。

要恢复这种行为(有谁知道为什么会改变它?),您可以添加:

return angular.extend($scope, this);

在控制器函数结束时(前提是$scope已注入到此控制器函数中)。

这有一个很好的效果,即通过控制器对象访问父范围,你可以用它来获取子对象require: '^myParentDirective'

评论

7赞 Robert Martin 1/8/2015
本文很好地解释了为什么这个和$scope不同。
28赞 Derek 8/29/2014 #4

我刚刚读到一个非常有趣的解释,关于两者之间的区别,以及越来越倾向于将模型附加到控制器并别名控制器以将模型绑定到视图。http://toddmotto.com/digging-into-angulars-controller-as-syntax/ 是文章。

注意:原始链接仍然存在,但格式的变化使其难以阅读。在原版中更容易查看。

他没有提到它,但是在定义指令时,如果你需要在多个指令之间共享一些东西,并且不想要一个服务(在合法的情况下,服务很麻烦),那么将数据附加到父指令的控制器。

该服务提供了许多有用的东西,这是最明显的东西,但是如果您只需要将数据绑定到视图,那么在模板中使用普通控制器和“控制器作为”就可以了,可以说是更可取的。$scope$watch

16赞 Sandro 9/24/2014 #5

在本课程(https://www.codeschool.com/courses/shaping-up-with-angular-js)中,他们解释了如何使用“这个”和许多其他东西。

如果通过“this”方法将方法添加到控制器,则必须在视图中使用控制器名称“dot”属性或方法调用它。

例如,在视图中使用控制器,您可能有如下代码:

    <div data-ng-controller="YourController as aliasOfYourController">

       Your first pane is {{aliasOfYourController.panes[0]}}

    </div>

评论

6赞 Matt Montag 12/11/2014
完成课程后,我立即对代码使用感到困惑,所以感谢您提及它。$scope
16赞 dumbledad 12/11/2014
那门课程根本没有提到$scope,他们只是使用,那么它如何帮助解释差异呢?asthis
10赞 Bruno Finger 3/11/2015
我第一次接触 Angular 是从上述课程开始的,正如从未提到的那样,我学会了只在控制器中使用。问题在于,当你开始在控制器中处理 promise 时,你会遇到很多引用问题,并且必须开始做一些事情,比如从 promise 返回函数中引用模型。所以正因为如此,我仍然对我应该使用哪种方法感到非常困惑,或者.$scopethisthisvar me = thisthis$scopethis
0赞 Dzmitry Lazerka 3/18/2016
@BrunoFinger 不幸的是,每当你做 Promise 或其他闭包繁重的东西时,你都需要。它与 Angular 无关。var me = this.bind(this)
2赞 William B 11/26/2016
重要的是要知道这等同于放入控制器本身 -- 它在作用域上定义了 MyCtrl 的实例 (this),以便在模板中使用ng-controller="MyCtrl as MC"$scope.MC = this{{ MC.foo }}
19赞 Liran Brimer 11/3/2015 #6

我建议你阅读以下帖子: AngularJS:“控制器作为”还是“$scope”?

它很好地描述了使用“Controller as”来公开变量而不是“$scope”的优势。

我知道你专门问了方法而不是变量,但我认为最好坚持一种技术并与之保持一致。

因此,在我看来,由于帖子中讨论的变量问题,最好只使用“控制器作为”技术并将其应用于方法。

评论

0赞 ggorlen 4/29/2023
链接似乎已失效
1赞 Aniket Jha 2/7/2018 #7

$scope有一个不同的“this”,然后是控制器“this”。因此,如果你把一个控制台.log(this)放在控制器里,它会给你一个对象(控制器),this.addPane()会把addPane方法添加到控制器对象中。但是$scope具有不同的作用域,其作用域中的所有方法都需要通过 $scope.methodName() 访问。 inside controller 意味着在控制器对象中添加 methos。在 HTML 中和内部this.methodName()$scope.functionName()

$scope.functionName(){
    this.name="Name";
    //or
    $scope.myname="myname"//are same}

将此代码粘贴到编辑器中,然后打开控制台查看...

 <!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">
    <title>this $sope vs controller</title>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.6.7/angular.min.js"></script>
    <script>
        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";
          }

        });
</script>
</head>
<body ng-app="myApp" >
<div ng-controller="ctrlExample">
       Comming From $SCOPE :{{firstName}}
       <br><br>
       Comming from $SCOPE:{{lastName}}
       <br><br>
       Should Come From Controller:{{nickName}}
       <p>
            Blank nickName is because nickName is attached to 
           'this' of controller.
       </p>

       <br><br>
       <button ng-click="controllerMethod()">Controller Method</button>

       <br><br>
       <button ng-click="show()">Show</button>
       <p>{{message}}</p>

   </div>

</body>
</html>