• $parse/$eval和$observe/$watch如何区分


    大家在看angular的时候,有时候偶尔会看到$parse,$eval和$observe,$watch这两对语法,随着深入使用angular,就不可避免使用到它。
    文章从内部运行机制跟实际需求的角度来解释这两对语法的区别。

    原理

    $parse和$eval

    首先,$parse跟$eval都是用来解析表达式的, 但是$parse是作为一个单独的服务存在的。$eval是作为scope的方法来使用的。
    $parse典型的使用是放在设置字符串表达式映射在真实对象上的值。也可以从$parse上直接获取到表达式对应的值。

    var getter = $parse('user.name'); 
    var setter = getter.assign; 
    setter(scope, 'new name');
    getter(context, locals) // 传入作用域,返回值
    setter(scope,'new name') // 修改映射在scope上的属性的值为‘new value’
    

    $eval 即scope.$eval,是执行当前作用域下的表达式,如:scope.$eval('a+b'); 而这个里的a,b是来自 scope = {a: 2, b:3};
    看看源码它的实现是

    $eval: function(expr, locals) {...}
    return $parse(expr)(this, locals); }
    ,
    

    可以找到它也是基于$parse,不过它的参数已经被固定为this,就是当前的scope,所以$eval只是在$parse基础上的封装而已,是一种$parse快捷的API。

    $observe和$watch

    $observe和$watch都可以用来对属性进行监控的,先来看下$observe源码:

    $observe: function(key, fn) {...}
    var attrs = this, $$observers = (attrs.$$observers || (attrs.$$observers = {...}
    }
    )), listeners = ($$observers[...]
    key]
    || ($$observers[...]
    key]
    = [...]
    ]
    )); listeners.push(fn); $rootScope.$evalAsync(function() {...}
    if (!listeners.$$inter) {...}
    // no one registered attribute interpolation function, so lets call it manually fn(attrs[...]
    key]
    ); }
    }
    ); return fn; }

    从这里可以看出来它是使用了$rootScope.$evalAsync()方法来监控的。什么是$evalAsync呢?是一个异步解析的操作,是在其他表达式都已经解析之后再解析,这样使它拥有了处理像插值字符串的机会。

    $observe是属性对象上的方法,因此它是用来监控DOM属性上的值的变化,它仅用在指令内部,当你需要在指令内部监控包含有插值表达式的DOM属性的时候,就要用到这个方法,

    比如,<code>attr1="Name:"</code>,然后在指令里面:<code>attrs.$observe('attr1', ....)</code>,但是假如你只用<code>scope.$watch(attrs.attr1,...)</code>,这种情况下是无效的,因为<code></code>无法被解析,所以你得到的是<code>undefined</code>, 在其他情况下用$watch。

    $watch更复杂一点,它可以监视表达式,这个表达式可以是函数或者字符串,假如表达式是字符串的话,会被封装成一个函数,然后在digest循环的时候被调用。 这个字符串表达式不能包含<code></code>,$watch是一个scope对象上的方法,所以它可以在任何你可以访问到作用域的地方被调用。比如,控制器中或者link函数中。因为字符串是被当做angular的表达式解析的,所以$watch经常被用在当你想要监控一个模型或者作用域对象的时候,

    比如:<code>attr1="myModel.some_prop"</code>,然后在控制器中或者link函数中<code>scope.$watch('myModel.some_prop',...)</code>或者<code>scope.$watch(attrs.attr1,...)</code>或者<code>scope.$watch(attrs['attr1'],...)</code>。假如你使用<code>attrs.$observe('attr1')</code>你就只能得到<code>myModel.some_prop</code>字符串的值。

    $observe和$watch都会在每个digest阶段被执行。

    带有隔离作用域的指令的情况会复杂点。假如'@'语法被使用的时候,你可以使用$observe或者$watch一个包含插值<code></code>的DOM属性,之所以$watch在这里也能用,是因为'@'语法为我们处理了,因此传给$watch是的时候是没有的字符串。这样子我们使用起来就更容易了。但是这种情况还是推荐使用$observe。

    想要更好的理解,大家可以看这个例子<a href="http://plnkr.co/edit/HBha8sVdeCqhJtQghGxw?p=preview">Plunker</a>。

    注意到,当link函数被执行的时候,任何包含的DOM属性都还没被解析,所以此时假如你检查这种方式定义的属性值的话,就是得到undefined, 唯一的方法可以得到内部的真实值的是使用$observe,或者在包含带有@语法的隔离作用域中使用$watch.因此获取这些属性的值是异步操作(因为要内部的值是链接到其他地方,所以要等这些值稳定之后,才能得到属性的正确值,而在上文的源码中你可以看到使用了$rootScope.$evalAsync())。

    有时候我们并不需要$observe和$watch,比如你的属性一个boolen类型或者数字类型的值,只要解析他们一次就够了,你可以这么做:<code>attr1="22"</code>, 同时你的链接函数: <code>var count = scope.$eval(attrs.attr1)</code>,假如只是包含字符串,<code>attr1="my string"</code>,你就可以直接使用<code>attrs.attr1</code>而不需要用$eval方法。

    使用场景

    想要在沒有isolate scope的directive中取出foo的属性值,存在以下几种情况:

    1. foo属性值是固定的字串值,例如想要傳 class name,id 等。
      <div foo="fadeOut"></div>
      
      因为这种情况是直接給定固定字串值,可以直接在 foo directive 中的 link function 直接取出属性值,attrs.foo。
    1. foo 属性值是非字串的值,例如:boolean, number 等。
      <div foo="true"></div>, <div foo="123"></div>
      
      如果通过 attrs.foo 直接取值的话,就只能取到字符串值,但是我想要取值后就是正确的类型的話,可以使用 scope.$eval(attrs.foo)。
    1. foo 属性是一個 scope property。

      angular.controller('BarCtrl', function($scope) {...}
      $scope.bar = {...}
      test: '' }
      ; }
      );
      // html
      <div ng-controller="BarCtrl">
      <div foo="bar.test"></div>
      </div>
      

      如果想要在 foo directive 中去改变bar.test的值的話,可以使用$parse來取值,$parse(attrs.foo),$parse后会一個 function,之後你可以通过这个function的assign property 去设置 bar.test 的值:

      var model = $parse(attrs.foo);
      model.assign(scope, 'Hello world');    // scope 为 link function 的 scope
      
    2. foo 属性值是 interpolated attribute。

      <div foo="2"></div>, <div foo=""></div>
      

      如果直接在 link function 直接使用 attrs.foo 取值的話,得到的是 undefined,因为此时 interpolated attribute 还没被解析,所以我們可以通过 $observe 來取值,attrs.$observe。

  • 相关阅读:
    什么是数据产品经理?需要什么能力?有哪些相关书籍可以读?
    Elasticsearch 文章合集
    大数据面试汇总
    产品经理要掌握的数据知识:数据的基本概念、术语、指标,基本技术和分析方法
    产品经理面试6个层面:做狐狸or刺猬?
    HDFS文章合集
    AppleApp(1):TextMate苹果中媲美Notepad++的文本编辑器
    Flex同Java通信BlazeDS入门图文详解(上)
    flex&java通信错误之一:Server.resource.unavailable
    cellForRowAtIndexPath不被执行的原因
  • 原文地址:https://www.cnblogs.com/zhepama/p/3753698.html
Copyright © 2020-2023  润新知