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表达式的。

加油!

上一篇:android 49 广播接收者中启动其他组件


下一篇:poj2262---素数(质数)的判断