JSON-jsgit
Douglas Crockford 是 JSON 的發明者,因此經過 DC 的代碼來學習 JSON 和 parser 絕對是上乘之選。這個倉庫裏面有四個 JS 文件,今天咱們先研究 json_parse.js。github
json_parse 定義了以下 API:json
json_parse(string) => object
json_parse(string, (key,value)=>newValue ) => object
複製代碼
今天咱們只研究第一種 API。數組
用 WebStorm 打開源碼方便閱讀,把主要函數摺疊起來,就會發現代碼結構很是清晰,完整結構以下:bash
var json_parse = (function(){
'use strict'
var at; // The index of the current character
var ch; // The current character
var escape = {...}
var text
var error = function(){...}
var next = function(){...}
var number = function(){...}
var string = function(){...}
var white = function(){...}
var word = function(){...}
var array = function(){...}
var object = function(){...}
var value = function(){...}
return function parser(source, reciver){...}
}())
複製代碼
代碼首先用一個當即執行函數造出一個局部做用域,ES 6 中咱們只須要用 block 和 let 代替就好了。微信
主要思路在最後一個 parser 函數裏,咱們來看一下:函數
return function (source, reviver) {
var result;
text = source;
at = 0;
ch = " ";
result = value();
white();
if (ch) {
error("Syntax error");
}
return result;
};
複製代碼
看起來毫無邏輯呀。學習
爲何我總是說「看源碼的投入產出比很低」呢,由於你須要看完全部代碼,才知道主要邏輯是在作什麼。ui
還好代碼很少,我看完以後總結做者的思路以下。spa
有三個重要的變量,ch、at 和 text
接下來咱們定義一個動做:吃。
好了,parser 的難點講完了,接下來就是細節了,假設 text 是字符串 { "name" : "Frank" },一次完整的邏輯以下
ch=" ",at=0, text='{ "name" : "Frank" }'
{
,就說明這是一個對象,生成一個空對象 object 用來存儲 key 和 value。並且後面的字符就要按照對象的語法來吃。{
後面應該接一個 "key"
,因此這個非空格必須是 "
。"
"
的字符(N >= 0)"
"key"
後面應該接 :
因此這個非空格必須是 :
:
"
,吃掉這個 "
,若是值是一個字符串"
的字符"
}
}
,吃掉 }
,說明 object 的數據已經讀完了若是你能在大腦裏過一遍這個過程,就能夠看懂全部源碼了:
var json_parse = (function(){
'use strict'
var at; // The index of the current character
var ch; // The current character
var escape = {...}
var text
var error = function(){...}
var next = 吃(){}
var number = 吃一個完整的數字(){...}
var string = 吃一個完整的字符串(){...}
var white = 吃N個空格(){...}
var word = 吃true/false/null這幾個單詞(){...}
var array = 吃一個完整的字符串(){...}
var object = 吃一個對象(){...}
var value = 吃一個值,包括對象數組字符串數組bool和null(){...}
return function parser(source, reciver){...}
}())
複製代碼
而後咱們就能夠重點看主邏輯了:
return function (source, reviver) {
var result;
text = source;
at = 0;
ch = " ";
result = value(); // 吃一個值
white(); // 吃掉後面的空格
if (ch) { // 若是空格後面還有字符,就是語法錯誤了
error("Syntax error");
}
return result;
};
複製代碼
也就是說主邏輯其實很簡單
接下來咱們看 value() 的邏輯
value = function () {
white();
switch (ch) {
case "{":
return object();
case "[":
return array();
case "\"":
return string();
case "-":
return number();
default:
return (ch >= "0" && ch <= "9")
? number()
: word();
}
};
複製代碼
邏輯也很簡單:
{
,就吃一整個對象,而後把對象返回[
,就吃一整個數組,而後把數組返回"
,就吃一整個字符串,而後把字符串返回-
,就吃一整個數字,而後把數字返回0
~9
,就吃一整個數字,而後把數字返回true
/false
/null
,見啥吃啥,而後返回圖示以下:
DC 用 ch >= "0" && ch <= "9"
來判斷字符是否是 0~9,這用到了 ASCII 字符集,若是你不懂就去搜一下。
你們應該對如何吃一個對象最感興趣,咱們來看看 object() 的邏輯
var object = function () {
var key;
var obj = {};
if (ch === "{") { // 當前字符必然是 {
next("{"); // 吃掉這個 {
white(); // 吃掉全部空格
if (ch === "}") { // 遇到 } 說明對象結束了
next("}"); // 吃掉這個 }
return obj; // 返回空對象
}
while (ch) { // 沒有遇到 } 說明有 key
key = string(); // 吃一個 string 當作 key
white(); // 吃掉全部空格
next(":"); // 吃掉一個 :
if (Object.hasOwnProperty.call(obj, key)) {
error("Duplicate key '" + key + "'");
} // 若是這個 key 以前遇到過就報錯
obj[key] = value();// 把key當作object的key,而後吃一個value做爲值
white(); // 吃掉全部空格
if (ch === "}") { // 若是遇到 } 說明對象結束了
next("}"); // 吃掉這個 }
return obj; // 返回對象
}
next(","); // 沒有遇到 } 說明還有 key,吃一個逗號
white(); // 吃掉空格而後繼續回到上面吃 key
}
}
error("Bad object"); // 若是運行到這裏說明語法有問題
};
複製代碼
到此咱們基本搞清楚 DC 的 json_parser 的思路了,你們能夠本身看一下 white()
、array()
的源碼,結構十分清晰。
下次咱們講 json_parse_state.js 如何使用狀態機的思路重寫了這個 parser。
個人微信公衆號:搜索 XDML 四個字母便可,XDML 是「寫代碼啦」的拼音首字母。