angular源码分析:angular中脏活累活的承担者之$interpolate
2015-11-20 17:26
633 查看
一、首先抛出两个问题
问题一:在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服务,将讲解指令是如何实现的,梳理指令的整个执行流程。
相关文章推荐
- AngularJS路由系列(2)--刷新、查看路由,路由事件和URL格式,获取路由参数,路由的Resolve
- angular学习笔记
- AngularJS的作用域Scope基础
- AngularJS 的 Scope
- Angularjs基础知识及示例汇总
- AngularJS路由系列(1)--基本路由配置
- AngularJS 学习笔记(2) AngularJS 表达式
- angularjs
- (笔记)angular material 选项卡用法
- (笔记)angular material radio用法
- angular
- AngularJS 问题&解决 | 技巧
- Angularjs快速入门(二)
- Angular使用中遇到的问题
- Angularjs快速入门(一)
- AngularJS过滤器filter入门
- 谈谈angularjs中的异步问题(一)
- Angularjs异步问题逻辑处理方法
- 关于angularjs中的异步请求
- ANGULAR学习笔记(三十一)-$LOCATION(1)