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 是「写代码啦」的拼音首字母。