如何测试对象是否为角度范围?

时间:2016-04-05 19:26:15

标签: javascript angularjs angularjs-scope

我试图在UI-Booststrap modal之上创建一个小的抽象层来删除重复的代码,工厂允许你像这样调用模态

modal.dialog({
    templateUrl: <String>
    scope: <Scope>
}). then(...);

我在我的工厂设置验证

if (settings && angular.isString(settings.templateUrl) ...)

但是当我试图验证<Scope>部分时,我运气不好。我找不到angular.isScope函数。

我该如何测试呢?

我知道,通过大量的工作,任何对象看起来都像$scope对象,如果你是偏执狂并尝试添加更多验证规则就像走下兔洞一样,你最终会丢失所以请不要这样做。我想要的是一种简单的方法,可以检测到相当数量的错误值来设置我的验证。

2 个答案:

答案 0 :(得分:3)

我开始查看角度代码。我找到了isScope函数来执行此操作

function isScope(obj) {
    return obj && obj.$evalAsync && obj.$watch;
}

基本上duck-typing检查对象是否类似于角度范围。这很简单,可用于大多数情况,但我想知道是否有更好的方法,比

更可靠
isScope({$watch:true ,$evalAsync:true})    => true

我发现每个范围对象都包含一个指向$root的{​​{1}}属性。这对于$rootScope本身也是有效的。

$rootScope

所以这仍然是鸭子打字,但比前一个好一点,因为我们使用identity来检查对象是否完全相同。将其移至可用的工厂

$rootScope.$root === $rootScope            => true

angular.module('app', [])
  .factory('isScope', function($rootScope) {
    return function(obj) {
      return obj && obj.$watch && obj.$evalAsync && obj.$root === $rootScope;
    };
  });
angular.module('app', [])
  .factory('isScope', function($rootScope) {
    return function(obj) {
      return obj && obj.$watch && obj.$evalAsync && obj.$root === $rootScope;
    };
  })
  .controller('TestController', function($scope, isScope) {
    $scope.resultObject = isScope({
      $watch: true,
      $evalAsync: true
    });
    $scope.resultScope = isScope($scope);
  });

当然这很容易被<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script> <div ng-app="app" ng-controller="TestController"> <div>Object: {{resultObject}}</div> <div>Scope: {{resultScope}}</div> </div>所愚弄,但在测试简单对象时会更好。

但是,我一直在寻找和阅读源代码,并发现所有范围都是使用名为Scope的函数语句创建的(显然是:P)。当然这个功能是私密的,不会暴露给外面的世界,或者是它!! ???

想象一下,当我看到它时,我感到惊讶

Scope angular function

这对创建新的作用域当然没用,有$rootScope.$new的api函数,但可以用于使用原型链测试作用域。

好的但等等,我知道孤立的范围没有原型地从其他范围继承,因此链将被破坏。事实证明,对于那些情况,角度使用new Scope()来实例化一个全新的隔离范围,因此这个技巧可能仍然有效,并且它会比以前的尝试更好。

{..., $root: $rootScope}

这将检测我们之前的尝试

function isScope(obj) {
    var Scope = $rootScope.constructor;
    return obj instanceof Scope;
}

将其移至可用工厂

console.log(isScope({..., $root: $rootScope}));    => false

angular.module('app', [])
    .factory('isScope', function($rootScope) {
        return function(obj) {
            return obj instanceof $rootScope.constructor;
        };
    });
angular.module('app', [])
  .factory('isScope', function($rootScope) {
    return function(obj) {
      return obj instanceof $rootScope.constructor;
    };
  })
  .controller('TestController', function($rootScope, $scope, isScope) {
    $scope.objectResult = isScope({
      $evalAsync: true,
      $watch: true,
      $root: $rootScope
    });
    $scope.scopeResult = isScope($scope);
  });

就像我说的,这不是失败证明,实际上很容易使用像

这样的东西
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app="app" ng-controller="TestController">
  <div>
    Object: {{objectResult}}
  </div>
  <div>
    Scope: {{scopeResult}}
  </div>
</div>

但足以测试大多数实际用例。当然,像instanceof的每次使用一样,在多个上下文(例如帧或窗口)中使用时会产生不良副作用,而且对于不同帧中的实际范围对象,这也会返回false,但这是一个非常极端的情况。不太可能发生。

答案 1 :(得分:1)

您可以检查对象是否是$ scope的实例。这不是完美的,但它可能就足够了。

if(scope instanceof $scope.constructor){ 
    /* enter code here */
}