为什么不推荐将$ rootScope与函数一起使用?

时间:2015-09-24 12:37:50

标签: javascript angularjs rootscope

在调查Angularjs的FEQ时,我在下面的文章中看到了:

  

$ rootScope存在,但它可以用于邪恶

     

Angular中的范围形成层次结构,原型继承自树顶部的根范围。通常这可以忽略,因为大多数视图都有自己的控制器,因此也有范围。

     

有时,您希望为整个应用程序提供全局数据。对于这些,您可以注入$rootScope并在其上设置值,就像任何其他范围一样。由于范围继承自根范围,因此这些值可用于附加到ng-show等指令的表达式,就像本地$scope上的值一样。

     

当然,全局状态很糟糕,你应该谨慎使用$rootScope,就像你希望用任何语言的全局变量一样。特别是,不要将它用于代码,只用于数据。如果您想在$rootScope上添加一个函数,那么将它放在一个可以在需要的地方注入的服务并且更容易测试时,几乎总是更好。

     

相反,不要创建一个服务,其唯一目的是存储和返回数据位。

     

— AngularJS FAQ - $rootScope exists, but it can be used for evil

所以我怀疑为什么$ rootScope不建议用作全局函数?有任何性能问题吗?

3 个答案:

答案 0 :(得分:10)

我过去曾回答过这个问题,但你提出这些问题是件好事。

  

$ rootScope存在,但它可以用于Angular中的恶作剧形式的层次结构,原型继承自树顶部的根作用域。通常这可以忽略,因为大多数视图都有自己的控制器,因此也有范围。

非隔离范围是分层的,但大多数开发人员应该使用具有隔离范围的指令。 AngularJS范围的分层性质是角度应用程序中许多错误的来源。这是一个我喜欢调用范围出血的问题,其中范围属性在DOM树中的某个地方被神奇地修改,你不知道为什么。

Angular的默认行为属于固有范围,这使得一个控制器很容易更新由另一个控制器管理的内容,依此类推,等等。这就是创建源代码之间的意大利面连接的方式。使维护代码非常困难。

  

有时,您希望为整个应用程序提供全局数据。对于这些,您可以像其他任何范围一样注入$ rootScope并在其上设置值。

不,这不正确。 AngularJS允许您定义常量,值和服务等内容。这些可以注入到路由,控制器和指令中。这就是你在全球范围内访问应用程序的方式,如果你想让你的控制器或指令可测试,你可以这样做。单元测试编写器不知道指令或控制器所依赖的$ rootScope中的属性。他们必须假设$ rootScope没有变异来提供服务或数据。

  

当然,全局状态很糟糕,你应该谨慎使用$ rootScope,就像你希望用任何语言的全局变量一样。

问题不在于$ rootScope,而在于人们正在做什么。许多应用程序将当前用户,身份验证令牌和会话数据添加到rootScope中。这最终会在模板中大量使用(如果用户登录则显示X,否则显示Y)。问题是HTML不会传达范围层次结构。因此,当您看到{{user.firstname + ' ' + user.lastname}}时,您不知道变量user来自何处。第二个问题是子范围可以影子根属性。如前面的示例所示,如果指令执行此操作scope.user = 'bla bla bla'。它没有替换rootScope上的值。它隐藏了它。现在你在模板中得到了一些奇怪的意外事情,你不知道为什么变量user已经改变了。

  

相反,不要创建一个服务,其唯一目的是存储和返回数据位。

Angular的$cacheFactory$templateCache是仅存储数据的服务示例。我认为作者试图鼓励在Angular的模块中使用常量和值,但这并不是一个很好的描述。

  

所以我怀疑为什么$ rootScope不建议用作全局函数?有任何性能问题吗?

$ rootScope是angular.config(..)期间唯一可用的范围。在此期间,如果这是唯一的时间,则可以修改范围。例如;您可能需要在应用开始之前注入API密钥或Google anayltics变量

任何范围的功能通常都是个坏主意。主要是因为范围中的所有内容都在模板中的表达式中被消化。功能可以隐藏繁重的操作。通过在调用函数时读取HTML,无法判断模板有多重。我已经看到了像getHeight()这样的范围函数,其中函数本身执行了3级嵌套循环。每次角度消化观察者以查看它是否发生变化时,必须调用该函数。您应该尽量将模板保持为 dry

答案 1 :(得分:3)

全局变量被滥用

$rootScope几乎是一个全局变量并且有它的位置,但绝大多数使用它的人都会滥用它。这些是一般不应使用Globals的原因。

  

非本地化 - 当各个元素的范围有限时,源代码最容易理解。全局变量可以被程序的任何部分读取或修改,使得难以记住或推理每种可能的用途。

     

无访问控制或约束检查 - 程序的任何部分都可以获取或设置全局变量,并且可以轻松地删除或忘记有关其使用的任何规则。 (换句话说,获取/设置访问器通常比直接数据访问更受欢迎,对于全局数据更是如此。)通过扩展,缺少访问控制极大地阻碍了在您可能希望运行不受信任的代码的情况下实现安全性(例如使用第三方插件)。

     

隐式耦合 - 具有许多全局变量的程序通常在这些变量之间存在紧密耦合,以及变量和函数之间的耦合。将耦合项目分组为有凝聚力的单元通常会带来更好的计划。

     

并发问题 - 如果多个执行线程可以访问全局变量,则需要进行同步(并且经常被忽略)。当动态链接模块与全局变量时,即使在几十个不同的上下文中测试的两个独立模块是安全的,组合系统也可能不是线程安全的。

     

命名空间污染 - 全球各地名称随处可见。当您认为使用本地(通过拼写错误或忘记声明本地)时,您可能会在不知不觉中最终使用全局,反之亦然。此外,如果您必须将具有相同全局变量名称的模块链接在一起,如果幸运的话,您将收到链接错误。如果您运气不好,链接器将只处理与同一对象同名的所有用途。

     

内存分配问题 - 某些环境具有内存分配方案,使得全局分配变得棘手。在构建者和#34;的语言中尤其如此。除了分配之外还有副作用(因为,在这种情况下,你可以表达两个全局相互依赖的不安全情况)。此外,在动态链接模块时,可能不清楚不同的库是否有自己的全局变量实例或者是否共享全局变量。

     

测试和限制 - 使用全局变量的来源有点难以测试,因为人们不能轻易地设置“清理”。运行之间的环境。更一般地,利用未明确提供给该源的任何类型的全局服务(例如,读取和写入文件或数据库)的源很难以相同的原因进行测试。对于通信系统,测试系统不变量的能力可能需要运行多个副本。系统同时受到任何使用共享服务(包括全局内存)的严重阻碍,这些服务并未作为测试的一部分提供共享。

来源http://c2.com/cgi/wiki?GlobalVariablesAreBad

以Angular

共享数据

在Angular中跨控制器共享数据时,您应该使用服务。使用自定义服务,您可以创建getter和setter方法。您将它注入您需要的控制器,并可以在您的应用程序中使用它。

答案 2 :(得分:1)

没有性能问题。它实际上会提高你的性能一小部分时间,因为你不需要依赖注入很多服务。

但它是设计的一个重要问题。考虑一个大型应用程序,它包含数十个视图,复杂组件并与许多众所周知的API相关联(例如Twitter,Flickr,Facebook,OAuth,......)。

您不会单独开发此应用程序。将出现以下问题:

命名空间

您正在使用Facebook API,其他人正在使用Twitter API。你们都认为使用$rootScope函数是一个好主意,并且都写了一个$rootScope.login函数。在git merge时你会如何解决这个问题?你需要命名空间,唉,你需要开发两个服务myFacebookAPImyTwitterAPI然后可以实现相同的loggin接口。(login(user,pw))。请注意,当您可以执行以下操作时,这使您能够抽象出您在控制器中处理的实际社交网络:

$scope.callAction = function (action) {
var service;
    if ($scope.serviceSelected === 'fb') {
         service = myFacebookAPI;
    } else {
         service = myTwitterAPI;
    }
    service[action]();
};

测试

在专业发展时,你会编写测试。 Angular为您提供了对服务等进行自动化测试的工具,但您无法以同样舒适的方式测试分配给$rootScope的内容。

其他问题也会出现,但我认为这应该足以让你自己思考。