这次我们来看一下angular的Sandboxing Angular Expressions。关于内置方法的,核心有两块:Lexer和Parser。其中大家对$parse可能更了解一点。好了不多废话,先看Lexer的内部结构:
1.Lexer
//构造函数 var Lexer = function(options) { this.options = options; }; //原型 Lexer.prototype = { constructor: Lexer, lex: function(){}, is: function(){}, peek: function(){ /* 返回表达式的下一个位置的数据,如果没有则返回false */ }, isNumber: function(){ /* 判断当前表达式是否是一个数字 */ }, isWhitespace: function(){/* 判断当前表达式是否是空格符 */}, isIdent: function(){/* 判断当前表达式是否是英文字符(包含_和$) */}, isExpOperator: function(){/* 判断当时表达式是否是-,+还是数字 */}, throwError: function(){ /* 抛出异常 */}, readNumber: function(){ /* 读取数字 */}, readIdent: function(){ /* 读取字符 */}, readString: function(){ /*读取携带''或""的字符串*/ } };
这里指出一点,因为是表达式。所以类似"123"这类的东西,在Lexer看来应该算是数字而非字符串。表达式中的字符串必须使用单引号或者双引号来标识。Lexer的核心逻辑在lex方法中:
lex: function(text) { this.text = text; this.index = 0; this.tokens = []; while (this.index < this.text.length) { var ch = this.text.charAt(this.index); if (ch === '"' || ch === "'") { /* 尝试判断是否是字符串 */ this.readString(ch); } else if (this.isNumber(ch) || ch === '.' && this.isNumber(this.peek())) { /* 尝试判断是否是数字 */ this.readNumber(); } else if (this.isIdent(ch)) { /* 尝试判断是否是字母 */ this.readIdent(); } else if (this.is(ch, '(){}[].,;:?')) { /* 判断是否是(){}[].,;:? */ this.tokens.push({index: this.index, text: ch}); this.index++; } else if (this.isWhitespace(ch)) { /* 判断是否是空白符 */ this.index++; } else { /* 尝试匹配操作运算 */ var ch2 = ch + this.peek(); var ch3 = ch2 + this.peek(2); var op1 = OPERATORS[ch]; var op2 = OPERATORS[ch2]; var op3 = OPERATORS[ch3]; if (op1 || op2 || op3) { var token = op3 ? ch3 : (op2 ? ch2 : ch); this.tokens.push({index: this.index, text: token, operator: true}); this.index += token.length; } else { this.throwError('Unexpected next character ', this.index, this.index + 1); } } } return this.tokens; }
主要看一下匹配操作运算。这里源码中会调用OPERATORS。看一下OPERATORS:
var OPERATORS = extend(createMap(), { '+':function(self, locals, a, b) { a=a(self, locals); b=b(self, locals); if (isDefined(a)) { if (isDefined(b)) { return a + b; } return a; } return isDefined(b) ? b : undefined;}, '-':function(self, locals, a, b) { a=a(self, locals); b=b(self, locals); return (isDefined(a) ? a : 0) - (isDefined(b) ? b : 0); }, '*':function(self, locals, a, b) {return a(self, locals) * b(self, locals);}, 'https://www.jb51.net/':function(self, locals, a, b) {return a(self, locals) / b(self, locals);}, '%':function(self, locals, a, b) {return a(self, locals) % b(self, locals);}, '===':function(self, locals, a, b) {return a(self, locals) === b(self, locals);}, '!==':function(self, locals, a, b) {return a(self, locals) !== b(self, locals);}, '==':function(self, locals, a, b) {return a(self, locals) == b(self, locals);}, '!=':function(self, locals, a, b) {return a(self, locals) != b(self, locals);}, '<':function(self, locals, a, b) {return a(self, locals) < b(self, locals);}, '>':function(self, locals, a, b) {return a(self, locals) > b(self, locals);}, '<=':function(self, locals, a, b) {return a(self, locals) <= b(self, locals);}, '>=':function(self, locals, a, b) {return a(self, locals) >= b(self, locals);}, '&&':function(self, locals, a, b) {return a(self, locals) && b(self, locals);}, '||':function(self, locals, a, b) {return a(self, locals) || b(self, locals);}, '!':function(self, locals, a) {return !a(self, locals);}, //Tokenized as operators but parsed as assignment/filters '=':true, '|':true });
可以看到OPERATORS实际上存储的是操作符和操作符函数的键值对。根据操作符返回对应的操作符函数。我们看一下调用例子:
var _l = new Lexer({}); var a = _l.lex("a = a + 1"); console.log(a);
结合之前的lex方法,我们来回顾下代码执行过程:
1.index指向'a'是一个字母。匹配isIdent成功。将生成的token存入tokens中
2.index指向空格符,匹配isWhitespace成功,同上
3.index指向=,匹配操作运算符成功,同上
4.index指向空格符,匹配isWhitespace成功,同上
5.index指向'a'是一个字母。匹配isIdent成功。同上
7.index指向+,匹配操作运算符成功,同上
8.index指向空格符,匹配isWhitespace成功,同上
9.index指向1,匹配数字成功,同上