亚洲激情专区-91九色丨porny丨老师-久久久久久久女国产乱让韩-国产精品午夜小视频观看

溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

$parse如何在Angularjs 1.3 中使用

發布時間:2021-01-21 16:16:24 來源:億速云 閱讀:106 作者:Leah 欄目:web開發

今天就跟大家聊聊有關$parse如何在Angularjs 1.3 中使用,可能很多人都不太了解,為了讓大家更加了解,小編給大家總結了以下內容,希望大家根據這篇文章可以有所收獲。

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 ch3 = ch + this.peek();
  var ch4 = ch3 + this.peek(2);
  var op1 = OPERATORS[ch];
  var op2 = OPERATORS[ch3];
  var op3 = OPERATORS[ch4];
  if (op1 || op2 || op3) {
   var token = op3 ? ch4 : (op2 ? ch3 : 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);},
 '/':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,匹配數字成功,同上

以上則是"a = a + 1"的代碼執行過程。9步執行結束之后,跳出while循環。剛才我們看到了,每次匹配成功,源碼會生成一個token。因為匹配類型的不同,生成出來的token的鍵值對略有不同:

number:{
  index: start,
  text: number,
  constant: true,
  value: Number(number)
 },
string: {
   index: start,
   text: rawString,
   constant: true,
   value: string
  },
ident: {
  index: start,
  text: this.text.slice(start, this.index),
  identifier: true /* 字符表示 */ 
 },
'(){}[].,;:?': {
 index: this.index,
 text: ch
},
"操作符": {
  index: this.index, 
  text: token, 
  operator: true
}
//text是表達式,而value才是實際的值

number和string其實都有相對應的真實值,意味著如果我們表達式是2e2,那number生成的token的值value就應該是200。到此我們通過lexer類獲得了一個具有token值得數組。從外部看,實際上Lexer是將我們輸入的表達式解析成了token json。可以理解為生成了表達式的語法樹(AST)。但是目前來看,我們依舊還沒有能獲得我們定義表達式的結果。那就需要用到parser了。

2.Parser

先看一下Parser的內部結構:

//構造函數
var Parser = function(lexer, $filter, options) {
 this.lexer = lexer;
 this.$filter = $filter;
 this.options = options;
};
//原型
Parser.prototype = {
 constructor: Parser,
 parse: function(){},
 primary: function(){},
 throwError: function(){ /* 語法拋錯 */},
 peekToken: function(){},
 peek: function(){/*返回tokens中的第一個成員對象 */},
 peekAhead: function(){ /* 返回tokens中指定成員對象,否則返回false */},
 expect: function(){ /* 取出tokens中第一個對象,否則返回false */ },
 consume: function(){ /* 取出第一個,底層調用expect */ },
 unaryFn: function(){ /* 一元操作 */},
 binaryFn: function(){ /* 二元操作 */},
 identifier: function(){},
 constant: function(){},
 statements: function(){},
 filterChain: function(){},
 filter: function(){},
 expression: function(){},
 assignment: function(){},
 ternary: function(){},
 logicalOR: function(){ /* 邏輯或 */},
 logicalAND: function(){ /* 邏輯與 */ },
 equality: function(){ /* 等于 */ },
 relational: function(){ /* 比較關系 */ },
 additive: function(){ /* 加法,減法 */ },
 multiplicative: function(){ /* 乘法,除法,求余 */ },
 unary: function(){ /* 一元 */ },
 fieldAccess: function(){},
 objectIndex: function(){},
 functionCall: function(){},
 arrayDeclaration: function(){},
 object: function(){}
}

Parser的入口方法是parse,內部執行了statements方法。來看下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 $parseStatements(self, locals) {
    var value;
    for (var i = 0, ii = statements.length; i < ii; i++) {
     value = statements[i](self, locals);
    }
    return value;
    };
  }
 }
 }

這里我們將tokens理解為表達式,實際上它就是經過表達式通過lexer轉換過來的。statements中。如果表達式不以},),;,]開頭,將會執行filterChain方法。當tokens檢索完成之后,最后返回了一個$parseStatements方法。其實Parser中很多方法都返回了類似的對象,意味著返回的內容將需要執行后才能得到結果。

看一下filterChain:

filterChain: function() {
 /* 針對angular語法的filter */
 var left = this.expression();
 var token;
 while ((token = this.expect('|'))) {
  left = this.filter(left);
 }
 return left;
 }

其中filterChain是針對angular表達式獨有的"|"filter寫法設計的。我們先繞過這塊,進入expression

expression: function() {
 return this.assignment();
 }

再看assignment:

assignment: function() {
 var left = this.ternary();
 var right;
 var token;
 if ((token = this.expect('='))) {
  if (!left.assign) {
  this.throwError('implies assignment but [' +
   this.text.substring(0, token.index) + '] can not be assigned to', token);
  }
  right = this.ternary();
  return extend(function $parseAssignment(scope, locals) {
  return left.assign(scope, right(scope, locals), locals);
  }, {
  inputs: [left, right]
  });
 }
 return left;
 }

我們看到了ternary方法。這是一個解析三目操作的方法。與此同時,assignment將表達式以=劃分成left和right兩塊。并且兩塊都嘗試執行ternary。

ternary: function() {
 var left = this.logicalOR();
 var middle;
 var token;
 if ((token = this.expect('?'))) {
  middle = this.assignment();
  if (this.consume(':')) {
  var right = this.assignment();
  return extend(function $parseTernary(self, locals) {
   return left(self, locals) ? middle(self, locals) : right(self, locals);
  }, {
   constant: left.constant && middle.constant && right.constant
  });
  }
 }
 return left;
 }

在解析三目運算之前,又根據?將表達式劃分成left和right兩塊。左側再去嘗試執行logicalOR,實際上這是一個邏輯與的解析,按照這個執行流程,我們一下有了思路。這有點類似我們一般寫三目時。代碼的執行情況,比如: 2 > 2 ? 1 : 0。如果把這個當成表達式,那根據?劃分left和right,left就應該是2 > 2,right應該就是 1: 0。然后嘗試在left看是否有邏輯或的操作。也就是,Parser里面的方法調用的嵌套級數越深,其方法的優先級則越高。好,那我們一口氣看看這個最高的優先級在哪?

logicalOR -> logicalAND -> equality -> relational -> additive -> multiplicative -> unary

好吧,嵌套級數確實有點多。那么我們看下unary。

unary: function() {
 var token;
 if (this.expect('+')) {
  return this.primary();
 } else if ((token = this.expect('-'))) {
  return this.binaryFn(Parser.ZERO, token.text, this.unary());
 } else if ((token = this.expect('!'))) {
  return this.unaryFn(token.text, this.unary());
 } else {
  return this.primary();
 }
 }

這邊需要看兩個主要的方法,一個是binaryFn和primay。如果判斷是-,則必須通過binaryFn去添加函數。看下binaryFn

binaryFn: function(left, op, right, isBranching) {
 var fn = OPERATORS[op];
 return extend(function $parseBinaryFn(self, locals) {
  return fn(self, locals, left, right);
 }, {
  constant: left.constant && right.constant,
  inputs: !isBranching && [left, right]
 });
 }

其中OPERATORS是之前聊Lexer也用到過,它根據操作符存儲相應的操作函數。看一下fn(self, locals, left, right)。而我們隨便取OPERATORS中的一個例子:

'-':function(self, locals, a, b) {
   a=a(self, locals); b=b(self, locals);
   return (isDefined(a) ? a : 0) - (isDefined(b) ? b : 0);
  }

其中a和b就是left和right,他們其實都是返回的跟之前類似的$parseStatements方法。默認存儲著token中的value。經過事先解析好的四則運算來生成最終答案。其實這就是Parser的基本功能。至于嵌套,我們可以把它理解為js的操作符的優先級。這樣就一目了然了。至于primay方法。塔刷選{ ( 對象做進一步的解析過程。

Parser的代碼并不復雜,只是函數方法間調用密切,讓我們再看一個例子:

var _l = new Lexer({});
var _p = new Parser(_l);
var a = _p.parse("1 + 1 + 2");
console.log(a()); //4

我們看下1+1+2生成的token是什么樣的:

[
{"index":0,"text":"1","constant":true,"value":1},{"index":2,"text":"+","operator":true},{"index":4,"text":"1","constant":true,"value":1},{"index":6,"text":"+","operator":true},{"index":8,"text":"2","constant":true,"value":2}
]

Parser根據lexer生成的tokens嘗試解析。tokens每一個成員都會生成一個函數,其先后執行邏輯按照用戶輸入的1+1+2的順序執行。注意像1和2這類constants為true的token,parser會通過constant生成需要的函數$parseConstant,也就是說1+1+2中的兩個1和一個2都是返回$parseConstant函數,通過$parseBinaryFn管理加法邏輯。

constant: function() {
 var value = this.consume().value;

 return extend(function $parseConstant() {
  return value; //這個函數執行之后,就是將value值返回。
 }, {
  constant: true,
  literal: true
 });
 },
binaryFn: function(left, op, right, isBranching) {
 var fn = OPERATORS[op];//加法邏輯
 return extend(function $parseBinaryFn(self, locals) {
  return fn(self, locals, left, right);//left和right分別表示生成的對應函數
 }, {
  constant: left.constant && right.constant,
  inputs: !isBranching && [left, right]
 });
 }

那我們demo中的a應該返回什么函數呢?當然是$parseBinaryFn。其中的left和right分別是1+1的$parseBinaryFn,right就是2的$parseConstant。

再來一個例子:

var _l = new Lexer({});
var _p = new Parser(_l);
var a = _p.parse('{"name": "hello"}');
console.log(a);

這邊我們傳入一個json,理論上我們執行完a函數,應該返回一個{name: "hello"}的對象。它調用了Parser中的object

object: function() {
 var keys = [], valueFns = [];
 if (this.peekToken().text !== '}') {
  do {
  if (this.peek('}')) {
   // Support trailing commas per ES5.1.
   break;
  }
  var token = this.consume();
  if (token.constant) {
   //把key取出來
   keys.push(token.value);
  } else if (token.identifier) {
   keys.push(token.text);
  } else {
   this.throwError("invalid key", token);
  }
  this.consume(':');
  //冒號之后,則是值,將值存在valueFns中
  valueFns.push(this.expression());
  //根據逗號去迭代下一個
  } while (this.expect(','));
 }
 this.consume('}');
 return extend(function $parseObjectLiteral(self, locals) {
  var object = {};
  for (var i = 0, ii = valueFns.length; i < ii; i++) {
  object[keys[i]] = valueFns[i](self, locals);
  }
  return object;
 }, {
  literal: true,
  constant: valueFns.every(isConstant),
  inputs: valueFns
 });
 }

比方我們的例子{"name": "hello"},object會將name存在keys中,hello則會生成$parseConstant函數存在valueFns中,最終返回$parseObjectLiternal函數。

下一個例子:

var a = _p.parse('{"name": "hello"}["name"]');

這個跟上一個例子的差別在于后面嘗試去讀取name的值,這邊則調用parser中的objectIndex方法。

objectIndex: function(obj) {
 var expression = this.text;
 var indexFn = this.expression();
 this.consume(']');
 return extend(function $parseObjectIndex(self, locals) {
  var o = obj(self, locals), //parseObjectLiteral,實際就是obj
   i = indexFn(self, locals), //$parseConstant,這里就是name
   v;
  ensureSafeMemberName(i, expression);
  if (!o) return undefined;
  v = ensureSafeObject(o[i], expression);
  return v;
 }, {
  assign: function(self, value, locals) {
  var key = ensureSafeMemberName(indexFn(self, locals), expression);
  // prevent overwriting of Function.constructor which would break ensureSafeObject check
  var o = ensureSafeObject(obj(self, locals), expression);
  if (!o) obj.assign(self, o = {}, locals);
  return o[key] = value;
  }
 });
 }

很簡單吧,obj[xx]和obj.x類似。大家自行閱讀,我們再看一個函數調用的demo

var _l = new Lexer({});
var _p = new Parser(_l, '', {});
var demo = {
 "test": function(){
 alert("welcome");
 }
};
var a = _p.parse('test()');
console.log(a(demo));

我們傳入一個test的調用。這邊調用了parser中的functionCall方法和identifier方法

identifier: function() {
 var id = this.consume().text;
 //Continue reading each `.identifier` unless it is a method invocation
 while (this.peek('.') && this.peekAhead(1).identifier && !this.peekAhead(2, '(')) {
  id += this.consume().text + this.consume().text;
 }
 return getterFn(id, this.options, this.text);
 }

看一下getterFn方法

...
forEach(pathKeys, function(key, index) {
  ensureSafeMemberName(key, fullExp);
  var lookupJs = (index
      // we simply dereference 's' on any .dot notation
      ? 's'
      // but if we are first then we check locals first, and if so read it first
      : '((l&&l.hasOwnProperty("' + key + '"))?l:s)') + '.' + key;
  if (expensiveChecks || isPossiblyDangerousMemberName(key)) {
  lookupJs = 'eso(' + lookupJs + ', fe)';
  needsEnsureSafeObject = true;
  }
  code += 'if(s == null) return undefined;\n' +
    's=' + lookupJs + ';\n';
 });
 code += 'return s;';
 /* jshint -W054 */
 var evaledFnGetter = new Function('s', 'l', 'eso', 'fe', code); // s=scope, l=locals, eso=ensureSafeObject
 /* jshint +W054 */
 evaledFnGetter.toString = valueFn(code);
...

這是通過字符串創建一個匿名函數的方法。我們看下demo的test生成了一個什么匿名函數:

function('s', 'l', 'eso', 'fe'){
if(s == null) return undefined;
s=((l&&l.hasOwnProperty("test"))?l:s).test;
return s;
}

這個匿名函數的意思,需要傳入一個上下文,匿名函數通過查找上下文中是否有test屬性,如果沒有傳上下文則直接返回未定義。這也就是為什么我們在生成好的a函數在執行它時需要傳入demo對象的原因。最后補一個functionCall

functionCall: function(fnGetter, contextGetter) {
 var argsFn = [];
 if (this.peekToken().text !== ')') {
  /* 確認調用時有入參 */
  do {
  //形參存入argsFn
  argsFn.push(this.expression());
  } while (this.expect(','));
 }
 this.consume(')');
 var expressionText = this.text;
 // we can safely reuse the array across invocations
 var args = argsFn.length ? [] : null;
 return function $parseFunctionCall(scope, locals) {
  var context = contextGetter ? contextGetter(scope, locals) : isDefined(contextGetter) ? undefined : scope;
  //或者之前創建生成的匿名函數
  var fn = fnGetter(scope, locals, context) || noop;
  if (args) {
  var i = argsFn.length;
  while (i--) {
   args[i] = ensureSafeObject(argsFn[i](scope, locals), expressionText);
  }
  }
  ensureSafeObject(context, expressionText);
  ensureSafeFunction(fn, expressionText);
  // IE doesn't have apply for some native functions
  //執行匿名函數的時候需要傳入上下文
  var v = fn.apply
   ? fn.apply(context, args)
   : fn(args[0], args[1], args[2], args[3], args[4]);
  if (args) {
  // Free-up the memory (arguments of the last function call).
  args.length = 0;
  }
  return ensureSafeObject(v, expressionText);
  };
 }

下面我們看一下$ParseProvider,這是一個基于Lex和Parser函數的angular內置provider。它對scope的api提供了基礎支持。

...
return function $parse(exp, interceptorFn, expensiveChecks) {
  var parsedExpression, oneTime, cacheKey;
  switch (typeof exp) {
  case 'string':
   cacheKey = exp = exp.trim();
   var cache = (expensiveChecks ? cacheExpensive : cacheDefault);
   parsedExpression = cache[cacheKey];
   if (!parsedExpression) {
   if (exp.charAt(0) === ':' && exp.charAt(1) === ':') {
    oneTime = true;
    exp = exp.substring(2);
   }
   var parseOptions = expensiveChecks ? $parseOptionsExpensive : $parseOptions;
   //調用lexer和parser
   var lexer = new Lexer(parseOptions);
   var parser = new Parser(lexer, $filter, parseOptions);
   parsedExpression = parser.parse(exp);
   //添加$$watchDelegate,為scope部分提供支持
   if (parsedExpression.constant) {
    parsedExpression.$$watchDelegate = constantWatchDelegate;
   } else if (oneTime) {
    //oneTime is not part of the exp passed to the Parser so we may have to
    //wrap the parsedExpression before adding a $$watchDelegate
    parsedExpression = wrapSharedExpression(parsedExpression);
    parsedExpression.$$watchDelegate = parsedExpression.literal ?
    oneTimeLiteralWatchDelegate : oneTimeWatchDelegate;
   } else if (parsedExpression.inputs) {
    parsedExpression.$$watchDelegate = inputsWatchDelegate;
   }
   //做相關緩存
   cache[cacheKey] = parsedExpression;
   }
   return addInterceptor(parsedExpression, interceptorFn);
  case 'function':
   return addInterceptor(exp, interceptorFn);
  default:
   return addInterceptor(noop, interceptorFn);
  }
 };

看完上述內容,你們對$parse如何在Angularjs 1.3 中使用有進一步的了解嗎?如果還想了解更多知識或者相關內容,請關注億速云行業資訊頻道,感謝大家的支持。

向AI問一下細節

免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

AI

浦东新区| 且末县| 藁城市| 桐乡市| 陈巴尔虎旗| 万载县| 浦北县| 德兴市| 阿图什市| 乌拉特中旗| 黄大仙区| 临邑县| 白水县| 温州市| 淳化县| 杨浦区| 云安县| 聂拉木县| 洞头县| 武胜县| 蒲城县| 句容市| 章丘市| 永丰县| 焦作市| 信丰县| 永城市| 金坛市| 峨边| 仁布县| 洛川县| 孟州市| 天长市| 西贡区| 茶陵县| 兰坪| 江永县| 泰顺县| 白河县| 南安市| 泸水县|