这些表达式之间的区别是什么?

时间:2014-11-13 02:50:56

标签: angularjs angularjs-scope

范围属性:

  • $scope.$watch('foo', fn)
  • $scope.$watch(function() {return $scope.foo}, fn)

非范围对象:

  • $scope.$watch(obj.prop, fn)
  • $scope.$watch(function() {return obj.prop}, fn)

具有非范围对象的对产生了不同的结果,因为在obj.prop更改时,前一个表达式没有执行。但为什么呢?

2 个答案:

答案 0 :(得分:1)

$scope.$watch('foo', fn)

这将使用$parse服务来观看$scope.foo的值,并将旧值与新值进行比较。

$scope.$watch(function() {return $scope.foo}, fn)

这与第一个相同,但使用lambda。 function() {return $scope.foo}将在每个$digest上执行,旧return值将与return新值进行比较。

$scope.$watch(obj.prop, fn)

这个很奇怪而且不推荐,因为它的行为完全取决于obj.prop的类型。例如,如果obj.prop === "foo",那么它将与$scope.$watch('foo', fn)相同。如果obj.prop === function(){ return Math.random(); }那么你有一个奇怪的事情。如果对于$watch的更改,您希望角度obj.prop obj.prop的值,则不会以这种方式工作。

$scope.$watch(function() {return obj.prop}, fn)

这个与$scope.$watch(function() {return $scope.foo}, fn)相同,因为 angular 将运行lambda function() {return obj.prop}每个$digest。旧return值将与新值进行比较。你在这里所拥有的最终是以正确的方式观看$scope上没有的东西。它会监视obj.prop的更改(因为它的return值。)

答案 1 :(得分:0)

我查看了代码,这就是它失败的原因:

$手表

$watch: function(watchExp, listener, objectEquality) {
  var get = $parse(watchExp);
  if (get.$$watchDelegate) {
    return get.$$watchDelegate(this, listener, objectEquality, get);
  }
  var scope = this,
      array = scope.$$watchers,
      watcher = {
        fn: listener,
        last: initWatchVal,
        get: get,
        exp: watchExp,
        eq: !!objectEquality
      };
  lastDirtyWatch = null;
  if (!isFunction(listener)) {
    watcher.fn = noop;
  }
  if (!array) {
    array = scope.$$watchers = [];
  }
  // we use unshift since we use a while loop in $digest for speed.
  // the while loop reads in reverse order.
  array.unshift(watcher);
  return function deregisterWatch() {
    arrayRemove(array, watcher);
    lastDirtyWatch = null;
  };
},

因此,当执行比较时,查看是否(value = watch.get(current)) !== (last = watch.last),它基本上调用get第二行中的$watch并将其新值与存储的值进行比较。要知道get是什么,我看了$parse

$解析

return function $parse(exp, interceptorFn) {
  var parsedExpression, oneTime, cacheKey;
  switch (typeof exp) {
    case 'string':
      cacheKey = exp = exp.trim();
      parsedExpression = cache[cacheKey];
      if (!parsedExpression) {
        if (exp.charAt(0) === ':' && exp.charAt(1) === ':') {
          oneTime = true;
          exp = exp.substring(2);
        }
        var lexer = new Lexer($parseOptions);
        var parser = new Parser(lexer, $filter, $parseOptions);
        parsedExpression = parser.parse(exp);
        if (parsedExpression.constant) {
          parsedExpression.$$watchDelegate = constantWatchDelegate;
        } else if (oneTime) {
          //oneTime is not part of the exp passed to the Parser so we may have to
          //wrap the parsedExpression before adding a $$watchDelegate
          parsedExpression = wrapSharedExpression(parsedExpression);
          parsedExpression.$$watchDelegate = parsedExpression.literal ?
            oneTimeLiteralWatchDelegate : oneTimeWatchDelegate;
        } else if (parsedExpression.inputs) {
          parsedExpression.$$watchDelegate = inputsWatchDelegate;
        }
        cache[cacheKey] = parsedExpression;
      }
      return addInterceptor(parsedExpression, interceptorFn);
    case 'function':
      return addInterceptor(exp, interceptorFn);
    default:
      return addInterceptor(noop, interceptorFn);
  }
};

基本上,观察表达式的类型有不同的情况。它有stringfunctiondefault

如果我看到的表达式obj.prop被评估为:

,这意味着什么
  1. 输入string,例如'foo',它将被视为$scope.foo案件。
  2. 输入function,它会在没有第二个参数的情况下传递到$parse(watchExp),而是按addInterceptor原样返回,并在每个$digest内执行,其返回值作为要比较的值。
  3. 其他类型,addInterceptor(noop, interceptorFn)将在没有interceptorFn的情况下处理。由于noop只是一个空的角度函数,因此这个函数不会做任何事情。
  4. 结论

    因此,如果我想检查obj.prop不属于$ scope的一部分,我应该使用返回值路由的函数$scope.$watch(function() {return obj.prop}, fn)