zoukankan      html  css  js  c++  java
  • angular源码分析:angular中脏活累活的承担者之$interpolate

    一、首先抛出两个问题

    问题一:在angular中我们绑定数据最基本的方式是用两个大括号将$scope的变量包裹起来,那么如果想将大括号换成其他什么符号,比如换成[{}],可不可以呢,如果可以在哪里配置呢?
    问题二:绑定的数据是如何被解析的呢?我们通过对$parse的分析,应该猜到绑定到模版的表达式最终会被传给$parse服务来处理,那么是谁将表达式从html字符串中给读取出来的呢?

    二、$interpolate的功能

    $interpolate是一个angular的内部服务,专门给$compile(等把$compile所依赖的服务讲完,我们就会分析$compile的代码了)调用的,而他的作业也比较简单:就是重字符中绑定的数据给解析出来。其中,它本身只完成获取数据表达式,表达式的解析将交给$parse服务来完成。
    为什么要说$interpolate和$parse干的一样是脏活累活呢?其实这里主要指的的累活,$interpolate将会被频繁调用,对代码的质量要求比较高。

    三、源代码

    1.$InterpolateProvider提供修改绑定数据时用的插入标记(interpolation markup)的能力

      var startSymbol = '{{';
      var endSymbol = '}}';
    
      this.startSymbol = function(value) {
        if (value) {
          startSymbol = value;
          return this;
        } else {
          return startSymbol;
        }
      };
    
      this.endSymbol = function(value) {
        if (value) {
          endSymbol = value;
          return this;//如果设置成功,返回对象的引用,提供优雅的链式书写能力
        } else {
          return endSymbol;
        }
      };
    

    解决第一个问题,我们可以通过在模块的配置代码中写入$InterpolateProvider.startSymbol('[{').endSymbol('}]')来将默认的插入标记改为[{}]

    2.插入标记被用来表示了绑定数据的开始和结束,那么,我们怎么来表示他们本身呢。

    请看下面的代码,初次看的时候,这里的代码有些难懂。

       var escapedStartRegexp = new RegExp(startSymbol.replace(/./g, escape), 'g'),//startSymbol.replace(/./g, escape)会给startSymbol插入三个反斜杠
            escapedEndRegexp = new RegExp(endSymbol.replace(/./g, escape), 'g');
    
        function escape(ch) {
          return '\\\' + ch;//因为转义的原因,ch前面的字符将是三个反斜杠符号
        }
    
        function unescapeText(text) {
          return text.replace(escapedStartRegexp, startSymbol).
            replace(escapedEndRegexp, endSymbol);
        }
    

    假设插入标记就是默认的{{}},escapedStartRegexp和escapedEndRegexp将会是什么?

    3.从字符串中解出表达式

    function $interpolate(text, mustHaveExpression, trustedContext, allOrNothing) {
          allOrNothing = !!allOrNothing;
          var startIndex,
              endIndex,
              index = 0,
              expressions = [],
              parseFns = [],
              textLength = text.length,
              exp,
              concat = [],
              expressionPositions = [];
    
          while (index < textLength) {
            if (((startIndex = text.indexOf(startSymbol, index)) != -1) &&
                 ((endIndex = text.indexOf(endSymbol, startIndex + startSymbolLength)) != -1)) {
              if (index !== startIndex) {
                concat.push(unescapeText(text.substring(index, startIndex)));
              }
              exp = text.substring(startIndex + startSymbolLength, endIndex);
              expressions.push(exp);
              parseFns.push($parse(exp, parseStringifyInterceptor));
              index = endIndex + endSymbolLength;
              expressionPositions.push(concat.length);
              concat.push('');
            } else {
              // we did not find an interpolation, so we have to add the remainder to the separators array
              if (index !== textLength) {
                concat.push(unescapeText(text.substring(index)));
              }
              break;
            }
          }
    ...
    

    从上面的代码实现来看,作者还是没有采用效率比较的低的正则表达式来完成表达式的识别。上面的代码完成的功能是将字符串分组(压入concat数组),分组的边界时表达式插入符,表达式本身用空格占位,表达式本则压入另一个数据(parseFns),并且记录下表达式在concat的位置。这里看到表达式的解析依然是调用的$parse服务。

    4.$interpolate最终返回的是什么?

          if (trustedContext && concat.length > 1) {
              $interpolateMinErr.throwNoconcat(text);
          }
    
          if (!mustHaveExpression || expressions.length) {
            var compute = function(values) { //计算表达式值,并且拼接为字符串
              for (var i = 0, ii = expressions.length; i < ii; i++) {
                if (allOrNothing && isUndefined(values[i])) return;
                concat[expressionPositions[i]] = values[i];
              }
              return concat.join('');
            };
    
            var getValue = function(value) { //获取值
              return trustedContext ?
                $sce.getTrusted(trustedContext, value) : //利用$sce进行检查
                $sce.valueOf(value);
            };
    
            return extend(function interpolationFn(context) {
                var i = 0;
                var ii = expressions.length;
                var values = new Array(ii);
    
                try {
                  for (; i < ii; i++) {
                    values[i] = parseFns[i](context);
                  }
    
                  return compute(values);
                } catch (err) {
                  $exceptionHandler($interpolateMinErr.interr(text, err));
                }
    
              }, {
              // all of these properties are undocumented for now
              exp: text, //just for compatibility with regular watchers created via $watch
              expressions: expressions,
              $$watchDelegate: function(scope, listener) { //监听的代理,通过Scope.$watchGroup对text中的所有表达式进行监听
                var lastValue;
                return scope.$watchGroup(parseFns, function interpolateFnWatcher(values, oldValues) {
                  var currValue = compute(values);
                  if (isFunction(listener)) {
                    listener.call(this, currValue, values !== oldValues ? lastValue : currValue, scope);
                  }
                  lastValue = currValue;
                });
              }
            });
          }
    
          function parseStringifyInterceptor(value) { 
            try {
              value = getValue(value);
              return allOrNothing && !isDefined(value) ? value : stringify(value);
            } catch (err) {
              $exceptionHandler($interpolateMinErr.interr(text, err));
            }
          }
        }
    

    $interpolate将返回一个函数,这个函数能够获取到text被解析后的值。这个函数并且绑定了exp,expressions和$$watchDelegate三个属性。

    上一期:angular源码分析:angular中入境检察官$sce
    下一期:angular源码分析:$compile服务——directive他妈
    ps:在下一期中,我们会讲解$compile服务,将讲解指令是如何实现的,梳理指令的整个执行流程。

  • 相关阅读:
    AX ERROR: Could not find my mock parent, most likely I am stale 不及格的程序员
    利用Segue在视图控制器间传值的问题 不及格的程序员
    Creating a Singleton Instance 不及格的程序员
    iPad 通知 UIKeyboardWillShowNotification 不会在keyBoard处在Undock状态下接到通知 不及格的程序员
    Why RootViewController's view is rotated Automatically by System when the app first loaded? 不及格的程序员
    如何弹出UIDatePicker最好 不及格的程序员
    jQuery开始做恶了 不及格的程序员
    what is the SEL,id and IMP,Class ,Method? 不及格的程序员
    Objectivec 字符串比较的陷井 不及格的程序员
    Unable to create any keyboard shortcuts after the iOS 6.1.3 update on iPad. 不及格的程序员
  • 原文地址:https://www.cnblogs.com/web2-developer/p/angular-12.html
Copyright © 2011-2022 走看看