AngularJS源码解析4:Parse解析器的详解
$ParseProvider简介
此服务提供者也是angularjs中用的比较多的,下面我们来详细的说下这个provider。
function $ParseProvider() {
var cache = {};
var $parseOptions = {
csp: false,
unwrapPromises: false,
logPromiseWarnings: true
};
this.unwrapPromises = function(value) {
if (isDefined(value)) {
$parseOptions.unwrapPromises = !!value;
return this;
} else {
return $parseOptions.unwrapPromises;
}
};
this.logPromiseWarnings = function(value) {
if (isDefined(value)) {
$parseOptions.logPromiseWarnings = value;
return this;
} else {
return $parseOptions.logPromiseWarnings;
}
};
this.$get = ['$filter', '$sniffer', '$log', function($filter, $sniffer, $log) {
.........
}];
}
记住,不管是哪个provider,我们都要先看它的$get属性。
this.$get = ['$filter', '$sniffer', '$log', function($filter, $sniffer, $log) {
$parseOptions.csp = $sniffer.csp;
promiseWarning = function promiseWarningFn(fullExp) {
if (!$parseOptions.logPromiseWarnings || promiseWarningCache.hasOwnProperty(fullExp)) return;
promiseWarningCache[fullExp] = true;
$log.warn('[$parse] Promise found in the expression `' + fullExp + '`. ' +
'Automatic unwrapping of promises in Angular expressions is deprecated.');
};
return function(exp) {
var parsedExpression;
switch (typeof exp) {
case 'string':
if (cache.hasOwnProperty(exp)) {
return cache[exp];
}
var lexer = new Lexer($parseOptions);
var parser = new Parser(lexer, $filter, $parseOptions);
parsedExpression = parser.parse(exp, false);
if (exp !== 'hasOwnProperty') {
cache[exp] = parsedExpression;
}
return parsedExpression;
case 'function':
return exp;
default:
return noop;
}
};
}];
在switch语句中可以看出,如果解析的是函数,则直接返回,如果是字符串,则需要对字符串进行解析。
代码中有两个关键类:
lexer,负责解析字符串,然后生成token,有点类似编译原理中的词法分析器
parser,负责对lexer生成的token,生成执行表达式,其实就是返回一个方法
switch中会先创建一个lexer实例对象,然后把lexer实例的对象传给parser的构造函数,生成parser实例对象,最后调用parser.parse方法生成执行表达式,实质是一个方法。
接下来,我们来看看parser.parse方法:
parse: function (text, json) {
this.text = text;
this.json = json;
this.tokens = this.lexer.lex(text);
if (json) {
this.assignment = this.logicalOR;
this.functionCall =
this.fieldAccess =
this.objectIndex =
this.filterChain = function() {
this.throwError('is not valid json', {text: text, index: 0});
};
}
var value = json ? this.primary() : this.statements();
if (this.tokens.length !== 0) {
this.throwError('is an unexpected token', this.tokens[0]);
}
value.literal = !!value.literal;
value.constant = !!value.constant;
return value;
},
里面的重点是this.tokens = this.lexer.lex(text);这句,它通过lexer解析字符串,生成token。
然后,执行var value = json ? this.primary() : this.statements();这句代码,默认情况下,json是false,所以会执行this.statements()方法,这里将会生成执行表达式。我们来看下parse的statements方法的源码:
statements: function() {
var statements = [];
while (true) {
if (this.tokens.length > 0 && !this.peek('}', ')', ';', ']'))
statements.push(this.filterChain());
if (!this.expect(';')) {
// optimize for the common case where there is only one statement.
// TODO(size): maybe we should not support multiple statements?
return (statements.length === 1)
? statements[0]
: function(self, locals) {
var value;
for (var i = 0; i < statements.length; i++) {
var statement = statements[i];
if (statement) {
value = statement(self, locals);
}
}
return value;
};
}
}
},
上面的代码中,有一个无限循环的while循环,此循环,会在return语句后,停止。
在while循环中,生成执行表达式的是filterChain方法返回的值,它被push到statements数组中。
最后,我们看return语句,也就是statements方法的返回值。如果表达式数组的长度为1,则返回第一个执行表达式,否则返回一个包装函数,里面是一个循环,循环处理所有的执行表达式,但是只返回最后一个表达式的值。
接下来,我们来看看lex方法:
lex: function (text) {
this.text = text;
this.index = 0;
this.ch = undefined;
this.lastCh = ':'; // can start regexp
this.tokens = [];
var token;
var json = [];
while (this.index < this.text.length) {
this.ch = this.text.charAt(this.index);
if (this.is('"\'')) {
this.readString(this.ch);
} else if (this.isNumber(this.ch) || this.is('.') && this.isNumber(this.peek())) {
this.readNumber();
} else if (this.isIdent(this.ch)) {
this.readIdent();
// identifiers can only be if the preceding char was a { or ,
if (this.was('{,') && json[0] === '{' &&
(token = this.tokens[this.tokens.length - 1])) {
token.json = token.text.indexOf('.') === -1;
}
} else if (this.is('(){}[].,;:?')) {
this.tokens.push({
index: this.index,
text: this.ch,
json: (this.was(':[,') && this.is('{[')) || this.is('}]:,')
});
if (this.is('{[')) json.unshift(this.ch);
if (this.is('}]')) json.shift();
this.index++;
} else if (this.isWhitespace(this.ch)) {
this.index++;
continue;
} else {
var ch2 = this.ch + this.peek();
var ch3 = ch2 + this.peek(2);
var fn = OPERATORS[this.ch];
var fn2 = OPERATORS[ch2];
var fn3 = OPERATORS[ch3];
if (fn3) {
this.tokens.push({index: this.index, text: ch3, fn: fn3});
this.index += 3;
} else if (fn2) {
this.tokens.push({index: this.index, text: ch2, fn: fn2});
this.index += 2;
} else if (fn) {
this.tokens.push({
index: this.index,
text: this.ch,
fn: fn,
json: (this.was('[,:') && this.is('+-'))
});
this.index += 1;
} else {
this.throwError('Unexpected next character ', this.index, this.index + 1);
}
}
this.lastCh = this.ch;
}
return this.tokens;
},
上面的代码主要就是分析传入到lex内的字符串text,主要通过一个while循环,依次检查当前字符是否是数字,是否是变量标识等,假如是数字的话,则转到lexer的
readNumber方法。最后,反正生成了一个token给Parser构造方法生成一个Parser实例对象parser。然后通过这个实例对象parser的parse来解析字符串表达式,生成执行表达式(其实就是一个方法)。
好了,说完了生成执行表达式的代码,其实parse的任务已经完成了。
在上一课的$RootScopeProvider源码解析中,有讲到,$RootScopeProvider的$watch函数中的compileToFn方法调用了$parse方法,而本课主要来讲解$parse如何通过解析字符串来生成执行表达式。
通过,这两课的讲解,大家知道了,angular内置的服务提供者,都带有$get属性,并且真正的定义也在$get属性值中。$RootScopeProvider服务提供者,是用来实例化一个根作用域对象的。
$ParseProvider服务提供者,是用来实例化一个解析器对象的,它主要用来解析页面中的angular表达式的。
加油!
AngularJS源码解析4:Parse解析器的详解的更多相关文章
- Spring源码分析之Bean的创建过程详解
前文传送门: Spring源码分析之预启动流程 Spring源码分析之BeanFactory体系结构 Spring源码分析之BeanFactoryPostProcessor调用过程详解 本文内容: 在 ...
- Spring源码剖析7:AOP实现原理详解
前言 前面写了六篇文章详细地分析了Spring Bean加载流程,这部分完了之后就要进入一个比较困难的部分了,就是AOP的实现原理分析.为了探究AOP实现原理,首先定义几个类,一个Dao接口: pub ...
- JDK源码分析(12)之 ConcurrentHashMap 详解
本文将主要讲述 JDK1.8 版本 的 ConcurrentHashMap,其内部结构和很多的哈希优化算法,都是和 JDK1.8 版本的 HashMap是一样的,所以在阅读本文之前,一定要先了解 Ha ...
- jQuery 源码分析(十) 数据缓存模块 data详解
jQuery的数据缓存模块以一种安全的方式为DOM元素附加任意类型的数据,避免了在JavaScript对象和DOM元素之间出现循环引用,以及由此而导致的内存泄漏. 数据缓存模块为DOM元素和JavaS ...
- Vue.js 源码分析(十) 基础篇 ref属性详解
ref 被用来给元素或子组件注册引用信息.引用信息将会注册在父组件的 $refs 对象上.如果在普通的 DOM 元素上使用,引用指向的就是 DOM 元素:如果用在子组件上,引用就指向组件实例,例如: ...
- vuex源码分析(二) state及strict属性 详解
state也就是vuex里的值,也即是整个vuex的状态,而strict和state的设置有关,如果设置strict为true,那么不能直接修改state里的值,只能通过mutation来设置 例1: ...
- Apache Spark源码走读之16 -- spark repl实现详解
欢迎转载,转载请注明出处,徽沪一郎. 概要 之所以对spark shell的内部实现产生兴趣全部缘于好奇代码的编译加载过程,scala是需要编译才能执行的语言,但提供的scala repl可以实现代码 ...
- spark最新源码下载并导入到开发环境下助推高质量代码(Scala IDEA for Eclipse和IntelliJ IDEA皆适用)(以spark2.2.0源码包为例)(图文详解)
不多说,直接上干货! 前言 其实啊,无论你是初学者还是具备了有一定spark编程经验,都需要对spark源码足够重视起来. 本人,肺腑之己见,想要成为大数据的大牛和顶尖专家,多结合源码和操练编程. ...
- jQuery 源码分析(十九) DOM遍历模块详解
jQuery的DOM遍历模块对DOM模型的原生属性parentNode.childNodes.firstChild.lastChild.previousSibling.nextSibling进行了封装 ...
- jQuery源码分析(九) 异步队列模块 Deferred 详解
deferred对象就是jQuery的回调函数解决方案,它解决了如何处理耗时操作的问题,比如一些Ajax操作,动画操作等.(P.s:紧跟上一节:https://www.cnblogs.com/grea ...
随机推荐
- C语言文件实现学生成绩管理
C语言实现学生成绩管理 项目简介 用C语言的链表及文件操作实现学生成绩的管理,实现主要的添加.修改.删除.查询的主要功能,并在程序关闭时将数据存储在二进制的文件中并加密.下一次打开程序,先解密二进制文 ...
- overloading与overriding的区别
overloading: “重载”,参数和返回值可改变. overriding: “覆盖”,重写父类的虚函数,参数和返回值必须与父类相同. 重写Overriding是父类与子类之间多态性的一种表现,重 ...
- js 滚动条滚动到底部触发事件
一.前言 在开发项目时,常常需要展示大量数据.如果全部显示出来,数据相对少时,看不出来什么不同,如果数据很多时,一次请求全部显示,这就相当可怕了. 面对这种问题,PC里使用了分页效果,将数据分成一页页 ...
- jQuery的表单选择器
1.常规选择器选择表单标签 $(function () { // var a = $("input").eq(0).val() // alert(a) // // var b = ...
- 将Halcon导出的多个dxf文件合并成一个分图层的dxf文件
Halcon中可以通过concat_obj合并多个XLD,并且可以通过write_contour_xld_dxf再将XLD保存为.dxf文件.但是,保存后的.dxf文件用AutoCAD打开后发现,它们 ...
- 【Centos linux系统】命令行(静默)安装oracle 11gR2
一.安装前准备 1.内存及swap要求 至于swap如何添加,后文将提到 1 2 grep MemTotal /proc/meminfo grep SwapTotal /proc/meminfo 2. ...
- win10自带虚拟机安装CentOS7系统(转)
出处:http://blog.csdn.net/bimabushihaodongxi/article/details/53677490 话说工欲善其事,必先利其器,在我准备学习Linux之前先要完成l ...
- 解决windows搭建jenkins执行selenium无法启动浏览器问题
因为jenkins是用windows installer 安装成windows的服务了,那么jenkins是一个后台服务,所以跑selium cases 的时候不显示浏览器 Step 1. Contr ...
- redis 设置认证
server 使用帮助 [root@VM_7_88_centos src]# ./redis-server -h Usage: ./redis-server [/path/to/redis.conf] ...
- C/C++编程可用的Linux自带工具
GNU Binary Utilities或binutils是一整套的编程语言工具程序,用来处理许多格式的目标文件.当前的版本原本由在Cygnus Solutions的程序员以Binary File D ...