-
Notifications
You must be signed in to change notification settings - Fork 383
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
JSON.parse 三种实现方式 #115
Comments
比较复杂的正则,写出来时间一久就看不懂啥意思了。 |
4: new Function() |
@AsJoy 感谢补充。 new Function 的确也可以,参考资料:神奇的eval()与new Function(), By 王昱森 |
@youngwind 就是模拟jsonP的方式拼接字符串然后以callBack的方式返回 |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
前言
近日在翻红宝书,看到 JSON 那一章节,忽然想到:“如何用 JS 实现 JSON.parse?”带着这个疑问,我找到了 JSON 之父 Douglas Crockford 写的 ployfill,里面提供了三种实现方式,下面我们逐一来分析。
Eval
第一种方式最简单,也最直观,就是直接调用 eval,代码如下:
因为 JSON 脱胎于 JS,同时也是 JS 的子集,所以能够直接交给 eval 运行。
然而,通常我们都说 eval 是邪恶的,尽量不要使用。为什么这里又用了呢?其实 eval 并不邪恶,只是对于新手来说,用了容易出问题,所以不建议使用而已。如果你水平够高,能正确地使用 Eval,那么它还是有很多用处的,比如静态模板。
ok,回到上面,我们像新手一样直接调用 eval,会不会出问题呢? → 会,这里有 XSS 漏洞。触发条件:参数 json 并非真正的 JSON 数据,而是可执行的 JS 代码。
那么,该如何规避这个问题呢?→ 老手 Douglas Crockford 给我们做了示范:对参数 json 做校验,只有真正符合 JSON 格式,才能调用 eval,具体就是下面这几个正则匹配。
看到上面的代码,你是否对那么复杂的正则感到头晕呢?反正我是很晕,所以我找了一个非常好用的正则可视化工具 Regexper 来帮我看懂这些正则,如下图所示。
有 2 个地方需要注意:
英文注释中提到:
表面上看起来要删除 open brackets 开括号
(
,而实际上正则 rx_four 匹配删除的却是[
,这是为什么呢?因为中英文语义的不同。在中文里,开括号一般指(
,而在英文里开括号一般指[
,其间细微差别需要知道。看 rx_three,里面有
(?:)
结构,这是正则的不捕获分组,具体可以参考这里。使用不捕获分组的原因:要解析的 json 有可能是一个很大的 JSON,如果匹配到的每个 token 都缓存起来的话,那么对内存的消耗是巨大的,而这里我们只想替换字符,并不需要知道都匹配到了哪些字符。拓展阅读:
递归
第一种 eval 的方法,相当于一股脑儿把 JSON 字符串塞进去。其实我们还可以手动逐个字符地扫描,然后进行判断,这就是第二种方法:递归。
所谓“递归”,就是重复调用 value 函数。
还是以
'{"a":"1", "b":2}'
为例,程序大致逻辑是:启动 → 首次调用value()
→ 发现是{
→ 原来是对象,走object()
→ 通过string()
得到 key 值为 "a" → 读取到冒号,哦,后面可能是对象、数组、布尔值等等,具体是什么,还得再次调用value()
才知道 → ……这种实现方案,既没有用 eval,也没有用正则,单纯靠逐个读取字符,所以代码逻辑比较复杂,需要多 debug 才能理清逻辑。lqt0223 也曾分析过这种实现方式:自己实现JSON、XML的解析 没那么难。
状态机
状态机名字起得很抽象,应用也非常广泛,比如正则引擎、词法分析,甚至是字符串匹配的 KMP 算法都能用它来解释。它代表着一种本质的逻辑:在 A 状态下,如果输入 B,就会转移到 C 状态。
那么,状态机与 JSON 字符串的解析有什么关系呢?→ JSON 字符串是有格式规范的,比如 key 和 value 之间用冒号隔开,比如不同 key-value 对之间用逗号隔开……这些格式规范可以翻译成状态机的状态转移,比如“如果检测到冒号,那么意味着下一步可以输入 value” 等等。还是以
'{"a":"1", "b":2}'
为例,我们来看看对这个 JSON 字符串进行解析时,状态机都流经了哪些状态。另外,这第三种实现方式,代码看起来非常的规整,是因为其广泛地应用了访问者模式,比如:
后话
看似简单的 JSON.parse,要实现起来也是大有可究之处。如果想顺便看 JSON.stringify 的实现方法,可以看 Douglas Crockford 版,也可以看 MDN 版,两者大同小异。另外,鉴于 Douglas Crockford 写的这个 JSON 库有些特殊情况没处理好,后来又出了一个新的库,名为 JSON3,它们之间的区别详见相关的讨论。
The text was updated successfully, but these errors were encountered: