JSON.parse() 的实现
1. JSON.parse()
JSON.parser() 是JSON 格式的一个函数, 它用于将object 数据类型转换成为JSON 数据类型, 这里我们来自己实现一下JSON.parser() 函数.
2. 前置知识
2.1 JSON格式中的数据类型
JSON 格式中, 可以将需要处理数据类型分为以下6类, 注意这里的意思是需要处理的属性类型有以下6 类, 表示的是数据处理的6 种情况;
真实的数据分类并不是按以下分类的, 这里需要注意.
- 字符串
- 数字
- 布尔值
- null 值
- 符号
- 包含转义字符的字符串(字符串和包含转义字符的字符串处理方式不同)
2.2 转义字符的处理
2.2 判断对象是否相等
自己实现一个函数, 用于判断两个对象是否相等, 实现代码如下:
// 判断两个对象是否相等的函数
const objectEquals = (a, b) => {
// 如果 a, b相等,则返回 true,否则返回 false
// Object.getOwnPropertyNames()方法返回一个由指定对象的所有自身属性的属性名(包括不可枚举属性但不包括Symbol值作为名称的属性)组成的数组
// 换句话来说 Object.getOwnPropertyNames()方法返回的是对象所有 key 组成的数组 list
var aProps = Object.getOwnPropertyNames(a)
var bProps = Object.getOwnPropertyNames(b)
if (aProps.length != bProps.length) {
return false
}
for (var i = 0; i < aProps.length; i++) {
var propName = aProps[i]
if (typeof a[propName] === 'object') {
let judge = objectEquals(a[propName], b[propName])
if (!judge) {
return false
}
} else if (a[propName] !== b[propName]) {
return false
}
}
return true
}
2.3 寻找匹配的字符串
这里匹配的字符串主要是括号的匹配, 理解下面的代码
// 返回对象 '{' 对应的 '}',返回数组 '[' 对应的 ']'
const getAimIndex = (string, index) => {
let str = string
// breakStr是结束判断字符
let startStr = str[index]
let breakStr = startStr === '{' ? '}' : ']'
let judgeNum = 0
/*
判断逻辑:
1. 遍历从 i = 1 开始
2. if temp = '{' || '[', judgeNum++
3. if temp = '}' || ']', judgeNum--
4. if temp = '}' || ']' && judgeNum === 1, return i
*/
for (let i = index, len = str.length; i < len; i++) {
let temp = str[i]
if (temp === startStr) {
judgeNum++
} else if (temp === breakStr && judgeNum !== 1) {
judgeNum--
} else if (temp === breakStr && judgeNum === 1) {
return i
}
}
// log('judgeNum: ', judgeNum)
}
let str = '{{{}}}'
console.log(getAimIndex(str, 0))
2.4 基础的递归思想
这个实现过程中, 很多内容的实现都需要递归去完成, 因此对于递归有基本的了解会比较好理解.
理解过程可能不会很费劲, 但是如果自己实现的话, 个人认为如果能够将 Title2.2 判断对象是否相等 独立实现就可以自己实现JSON.parse()
3. 实现流程
3.1 将JSON 字符串解析成为tokens 数组
在生产tokens 数组中, 直观上, 我们会认为我们只需要将JSON 中有意义的字符串添加在tokens 数组中即可, 但是, 这样做会存在一个问题:
对于如下代码
{
"obj1": true,
"obj2": "true"
}
上面代码中, obj1 obj2 属性本质上是不一样的, 一个是String 类型, 一个是Boolean 类型, 而我们从JSON 中读取的内容类型均为String 类型; 因此为了确保完美解析JSON 数据, 我们必须在tokens 数组中存储JSON 数据的值类型
同时, 由于我们生成对象的时候必须依赖值的类型, 值本身去生成对象, 因此需要提供一个方法返回值内容
综上所述, 我们使用面向对象的思想来解决这个问题:
const log = console.log.bind(console)
const TYPE = {
"[object Number]": 1,
"[object String]": 2,
"[object Null]": 3,
"[object Boolean]": 4,
"character": 5,
"escapeCharater": 6
}
const includeEscapeCharacter = function(str) {
let asciiEscape = [0, 39, 34, 92, 10, 13, 11, 9, 8, 12]
for (let i = 0, len = str.length; i < len; i++) {
let temp = str[i].charCodeAt(0)
if (asciiEscape.indexOf(temp) !== -1) {
return true
}
}
return false
}
const dealEscape = (str) => {
let escape = {
b: `\b`,
f: '\f',
n: '\n',
r: '\r',
t: '\t',
'\\': '\\',
'\/': '\/',
'\"': '\"',
"\'": "\'"
}
let result = ''
let string = str
let i = 0
let len = string.length
// log(str)
while (i < len) {
let temp = string[i]
if (temp === '\\') {
let endIndex = i + 1
result += escape[string[endIndex]]
i = endIndex + 1
} else {
result += temp
i++
}
}
return result
}
const getType = function (value) {
if (value === ':' || value === '{' || value === '}' || value === '[' || value === ']') {
return 5
}
if (includeEscapeCharacter(value)) {
return 6
}
let type = Object.prototype.toString.apply(value)
return TYPE[type]
}
class JsonValue {
constructor(value) {
this.value = String(value)
this.type = getType(value)
}
toString() {
if (this.type === 1) {
return Number(this.value)
} else if (this.type === 2) {
return String(this.value)
} else if (this.type === 3) {
return null
} else if (this.type === 4) {
return this.value === 'true'
} else if (this.type === 5) {
return String(this.value)
} else if (this.type === 6) {
return dealEscape(this.value)
}
}
}
const __main = () => {
let a = new JsonValue('a')
let b = new JsonValue(1)
log(a)
log(a.toString())
log(b)
log(b.toString())
}
__main()
tokens 数组中存储的都是JsonValue 类型的数据;
接下来遍历JSON 数据添加内容即可
3.2 将tokens 数组拼接成为Object 对象
这个过程就是遍历tokens 数组, 依照符号(:)的索引index, tokens[index - 1] 为key, tokens[index + 1] 为value; 找到key-value 对, 然后添加到结果即可; 需要注意的是这个过程需要考虑Object 的嵌套, 以及Array中Object 的嵌套, 这里需要使用递归去处理
这个过程的核心代码如下: 根据不同的情况, 处理不同的内容
// 获取key-value对的value值
const getTokensValue = (index, array) => {
let judge = array[index].value
if (judge === '{') {
let nextIndex = getAimIndex(array, index)
let sliceList = array.slice(index + 1, nextIndex)
return parsedDict(sliceList)
} else if (judge === '[') {
let nextIndex = getAimIndex(array, index)
let sliceList = array.slice(index + 1, nextIndex)
return conversionObj(sliceList)
} else {
return array[index].toString()
}
}
4. code
代码地址: JSON.parse() 的实现
JSON.parse() 的实现的更多相关文章
- JSON.parse()和JSON.stringify()
1.parse 用于从一个字符串中解析出json 对象.例如 var str='{"name":"cpf","age":"23&q ...
- JSON.stringify()与JSON.parse()
JSON.stringify()用于把一个对象解析成字符串,如 var student = { age: 23, name: 'wang' } JSON.stringify(student); 结果: ...
- JSON.parse 与 eval() 对于解析json的问题
1.eval()与JSOn.parse的不同 eval() var c = 1; //全局变量 var jsonstr1 = '{"name":"a",&quo ...
- JSON.parse与eval的区别
JSON.parse与eval和能将一个字符串解析成一个JSON对象,但还是有挺大区别. 测试代码 var A = "{ a: 1 , b : 'hello' }"; var B ...
- ajex请求的数据 什么时候需用Json.parse()
ajex请求的数据 什么时候需用 Json.parse()
- JSON.parse和eval的区别
JSON.parse和eval的区别 JSON(JavaScript Object Notation)是一种轻量级的数据格式,采用完全独立于语言的文本格式,是理想的数据交换格式.同时,JSON是Jav ...
- JSON.stringify()和JSON.parse()
parse用于从一个字符串中解析出json对象,如 var str = '{"name":"huangxiaojian","age":&qu ...
- JSON.parse()和JSON.stringify()区别
parse用于从一个字符串中解析出json对象,如: var str = '{"name":"huangxiaojian","age":&q ...
- JSON.stringify() / JSON.parse()
JSON.stringify() 这个方法可以把javascript对象转换成json字符串. JSON.parse() 这个方法可以把 json 字符串转换成 javascript对象. [下面来看 ...
- js中解析json对象:JSON.parse()用于从一个字符串中解析出json对象, JSON.stringify()用于从一个对象解析出字符串。
JSON.parse()用于从一个字符串中解析出json对象. var str = '{"name":"huangxiaojian","age&quo ...
随机推荐
- SAP HTTP调用其他系统接口
1业务说明 ABAP系统通过HTTP方式调用其他系统发布的接口 2代码实现 2.1认证接口 根据访问的URL创建HTTP客户端 设置访问方式,并调用SEND和接收函数 有时需要专门验证用户名密码 获取 ...
- 《C程序设计语言》 练习2-6 及 位运算总结
问题描述 2.6 编写一个函数setbits(x, p ,n, y),该函数返回对x执行下列操作后的结果值: 将x中从第p位开始的n个(二进制)位设置为y中最右边n位的值,x的其余各位保持不变. Wr ...
- flush方法和close方法的区别
package com.yhqtv.demo05.Writer; import java.io.FileWriter; /* * @author XMKJ yhqtv.com Email:yhqtv@ ...
- 初探Redis-基础类型Hash
Redis存在五种基础类型:字符串(String).队列(List).哈希(Hash).集合(Set).有序集合(Sorted Set).本次列举出Hash的常用操作. Redis官网:https:/ ...
- [hdu4300] next数组的应用
题意:给你一个密文和明文的对应表以及一个密文+明文的字符串,明文可能只出现前面的一部分(也就是说是原明文的前缀),求最短的明文. 思路:首先密文的长度至少占到一半,所以先把那一半解密,问题转化为找一个 ...
- linux --开机自动挂载硬盘【转】
转:http://c.biancheng.net/view/900.html 了解了 mount 命令之后,读者可能会问,系统如何在开机时自动挂载硬盘,它又是怎么知道哪些分区是需要挂载的呢? 很简单, ...
- POJ3693 Maximum repetition substring 后缀数组
POJ - 3693 Maximum repetition substring 题意 输入一个串,求重复次数最多的连续重复字串,如果有次数相同的,则输出字典序最小的 Sample input ccab ...
- [A*算法]基于Unity实现A*算法(二)
写在前面:上一篇当时是非常简单的了解一下A*,昨天还有一些问题没解决,就暂时把自己查阅的文坛摘抄了过来(毕竟人家写的比我要好的多 :> ) 今天终于解决了,就又写了这一篇,正好我自己再梳理一遍, ...
- EMAIL、用户名测试点
EMAIL xxxaa@xxx.xxx 1.没有@情况,如:aa.net 2.没有.符号,如:aa@qqcom 3..后面没有字符:如 xxxaa@xxx. 4..不在@后面, 如:xxxaa.@xx ...
- Spring Bean 后置处理器
Bean 后置处理器允许在调用初始化方法前后对 Bean 进行额外的处理. BeanPostProcessor 接口定义回调方法,你可以实现该方法来提供自己的实例化逻辑,依赖解析逻辑等. 你也可以在 ...