一、首先抛出两个问题

问题一:在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服务,将讲解指令是如何实现的,梳理指令的整个执行流程。

angular源码分析:angular中脏活累活的承担者之$interpolate的更多相关文章

  1. angular源码分析:angular中脏活累活承担者之$parse

    我们在上一期中讲 $rootscope时,看到$rootscope是依赖$prase,其实不止是$rootscope,翻看angular的源码随便翻翻就可以发现很多地方是依赖于$parse的.而$pa ...

  2. angular源码分析:angular中入境检察官$sce

    一.ng-bing-html指令问题 需求:我需要将一个变量$scope.x = '<a href="http://www.cnblogs.com/web2-developer/&qu ...

  3. angular源码分析:angular中$rootscope的实现——scope的一生

    在angular中,$scope是一个关键的服务,可以被注入到controller中,注入其他服务却只能是$rootscope.scope是一个概念,是一个类,而$rootscope和被注入到cont ...

  4. angular源码分析:$compile服务——directive他妈

    一.directive的注册 1.我们知道,我们可以通过类似下面的代码定义一个指令(directive). var myModule = angular.module(...); myModule.d ...

  5. angular源码分析:injector.js文件分析——angular中的依赖注入式如何实现的(续)

    昨天晚上写完angular源码分析:angular中jqLite的实现--你可以丢掉jQuery了,给今天定了一个题angular源码分析:injector.js文件,以及angular的加载流程,但 ...

  6. angular源码分析:angular中jqLite的实现——你可以丢掉jQuery了

    一.从function JQLite(element)函数开始. function JQLite(element) { if (element instanceof JQLite) { //情况1 r ...

  7. angular源码分析:angular中各种常用函数,比较省代码的各种小技巧

    angular的工具函数 在angular的API文档中,在最前面就是讲的就是angular的工具函数,下面列出来 angular.bind //用户将函数和对象绑定在一起,返回一个新的函数 angu ...

  8. angular源码分析:angular中的依赖注入式如何实现的

    一.准备 angular的源码一份,我这里使用的是v1.4.7.源码的获取,请参考我另一篇博文:angular源码分析:angular源代码的获取与编译环境安装 二.什么是依赖注入 据我所知,依赖注入 ...

  9. angular源码分析:angular的整个加载流程

    在前面,我们讲了angular的目录结构.JQLite以及依赖注入的实现,在这一期中我们将重点分析angular的整个框架的加载流程. 一.从源代码的编译顺序开始 下面是我们在目录结构哪一期理出的an ...

随机推荐

  1. 【hbase0.96】基于hadoop搭建hbase的心得

    hbase是基于hadoop的hdfs框架做的分布式表格存储系统,所谓表格系统就是在k/v系统的基础上,对value部分支持column family和column,并支持多版本读写. hbase的工 ...

  2. VMware Workstation and Hyper-V are not compatible. 解决方案

    VMware 和 Hyper-V 不能共存问题报错如下:VMware Workstation and Hyper-V are notcompatible. Remove the Hyper-V rol ...

  3. 移动web app开发必备 - zepto事件问题

    问题描述: 项目在祖先元素上绑定了 touchstart,touchmove,touchend事件,用来处理全局性的事件,比如滑动翻页 正常状态下: 用户在子元素上有交互动作时,默认状态下都是会冒泡到 ...

  4. javascript运动系列第八篇——碰壁运动

    × 目录 [1]匀速碰壁 [2]自由落体 [3]投掷碰壁[4]拖拽碰壁 前面的话 碰撞运动可能是运动系列里面比较复杂的运动了.碰撞可以分为碰壁和互碰两种形式,而碰撞前后的运动形式也可以分为变速和匀速两 ...

  5. Android NDK开发初识

    神秘的Android NDK开发往往众多程序员感到兴奋,但又不知它为何物,由于近期开发应用时,为了是开发的.apk文件不被他人解读(反编译),查阅了很多资料,其中有提到使用NDK开发,怀着好奇的心理, ...

  6. android图片透明度跟缩放大小动画事件

    概序 : 动画事件写在xml中,然后用AnimationUtils去加载动画事件,再监听动画结束事件,隐藏imageview. 1. player_double_click_animation.xml ...

  7. 百度sdk定位不成功,关闭定位

    公司项目有用到百度地图,登录的时候需要定位一次,获取登录的地址信息,在手机无法连接外网的情况,也就无法访问百度定位服务器的时候,定位的回调函数要30秒以上才能返回结果,于是去仔细查百度api,发现没有 ...

  8. Ubuntu杂记——链接ln的使用:创建和删除符号链接

    原文链接:http://blog.csdn.net/janpylx/article/details/6761910 一 . 使用方式 ln [option] source_file dist_file ...

  9. 【记录】JS 生成 URL 二维码

    示例代码: <html> <head> <title>example</title> </head> <body> <sc ...

  10. 倾情大奉送--Spark入门实战系列

    这一两年Spark技术很火,自己也凑热闹,反复的试验.研究,有痛苦万分也有欣喜若狂,抽空把这些整理成文章共享给大家.这个系列基本上围绕了Spark生态圈进行介绍,从Spark的简介.编译.部署,再到编 ...