手把手用代码教你实现JSON.parse

什么是 JSON

JSON(JavaScript Object Notation)是一种轻量的数据格式,它不是一门编程语言。JSON是基于JavaScript Programming Language,Standard ECMA-262 3rd Edition - December 1999的一个子集。但 JSON 并不属于 JavaScript,不少编程语言都有针对 JSON 的解析器和序列化器。javascript

JSON 语法

根据红宝书中的介绍,JSON 有三种类型的值,分别为简单值、对象和数组。html

  • 简单值:使用与 JavaScript 相同的语法,能够在 JSON 中表示字符串、数值、布尔值和 null。但 JSON 不支持 JavaScript 中的特殊值 undefined
  • 对象:对象做为一种复杂数据类型,表示的是一组无序的键值对儿。而每一个键值对儿中的值可 以是简单值,也能够是复杂数据类型的值。
  • 数组:数组也是一种复杂数据类型,表示一组有序的值的列表,能够经过数值索引来访问其中 的值。数组的值也能够是任意类型——简单值、对象或数组。

在 JavaScript 中已经内置了 JSON 对象,它有 parsestringify 两个方法。java

image.png

在实际工做中,这两个方法咱们也常常用到,例如实现对象深拷贝时编程

const obj = {
  name: 'Jay',
  age: 41
}
const jsonStr =  JSON.stringify(obj) // {"name":"Jay","age":41}
const copiedObj = JSON.parse(jsonStr) // {name: "Jay", age: 41}

本文就来实现 JSONparse 方法。json

实现 parse

咱们先看一下 JSON 字符串的结构数组

const json = `
{ 
  "status": 100,
  "msg": "返回成功",
  "data": { 
    "string": "abc",
    "array": [1,2,3], 
    "children": [ 
      { "name": "Jay", "age": 41, "occupation": "Musician"}, 
      { "name": "Jack", "age": 56, "occupation": "CEO"}, 
      { "name": "Kobe", "age": 42, "occupation": "Basketball players"}
    ]
  } 
}
`;

咱们的函数就叫作 fakeParseJSON,咱们先用原生方法跑一下编程语言

const fakeParseJSON = JSON.parse
fakeParseJSON(json) // {status: 100, msg: "返回成功", data: {…}}

咱们先从简单值开始来写函数

parseValue

值(value)能够是双引号括起来的字符串(string)、数值(number)、 truefalsenull、对象(object)或者数组(array)。这些结构能够嵌套。

流程图以下:测试

以值为 string 类型为例spa

const str = `
  "hello world"
`

上面就是一个简单的 JSON 值(value),根据流程图,从左往右会通过 whitespace, ", string, ", whitespace。咱们就一个一个来处理。

function fakeParseJSON(str) {
  let i = 0
  
  // 处理 whitespace, 遇到空格,回车,制表符等直接跳过
  function parseWhiteSpace() {
    while(str[i] === ' ' || str[i] === '\n' || str[i] === '\r' || str[i] === '\t') {
      i++
    }
  }
  
  function parseValue() {
    // 首先处理前面可能有的空格
    parseWhiteSpace()
    // 处理 string
    if(str[i] === '"') { // 以双引号开头
      i++
      let res = ''
      while(str[i] !== '"') {
        res += str[i]
        i++
      }
      // 继续往下移
      i++
      return res
    }
  }
}

测试一下

fakeParseJSON(str) // hello world

咱们的 JSON 值的类型不只有 string,还有 number, object 等类型。咱们最后要处理的都是 JSONvalue,并且咱们知道 valueobject 类型是“名称/值”对的集合形式,名称通常都是字符串,值的话各类类型都有。在解析 JSON 对象时,咱们要处理名称,这里咱们先单独抽离一个专门处理字符串的函数 parseString,咱们改动一下代码

function fakeParseJSON(str) {
  let i = 0
  
  // 处理 whitespace,
  // ...
  
  // 处理字符串
  function parseString() {
    parseWhiteSpace()
    if(str[i] === '"') { // 以双引号开头
      i++
      let res = ''
      while(str[i] !== '"') {
        res += str[i]
        i++
      }
      // 继续往下移
      i++
      return res
    }
  }
  
  // 处理结果
  function parseValue() {
     return parseString()
  }
  // 输出结果
  return parseValue()
}

parseObject

接下来咱们来处理 JSON 对象,先看看流程图

从图中咱们能够看出是以"{" 开头,"}"结尾,中间可能会经历 whitespace, string, :, whitespace, value, ,, ...。咱们也先以值为简单类型的为例

const obj = `
{
  "msg": "返回成功"
}
`

咱们也加上一个 parseObject 的函数

function fakeParseJSON(str) {
  let i = 0
  
  // 处理 whitespace
  // ...
  
  // 处理冒号
  function parseColon() {
    if(str[i] !== ":") {
      throw new Error('Expected ":".')
    }
    i++
  }
  
  // 处理字符串
  // ...
  
  // 处理对象
  function parseObject() {
    parseWhiteSpace()
    if(str[i] === '{') {
      i++
      parseWhiteSpace()
      const result = {}
      while(str[i] !== '}') { 
        // 处理字符串
        const key = parseString()
        parseWhiteSpace()
        parseColon() // 这里新加一个处理冒号的
        const value = parseValue()
        result[key] = value
      }
      i++
      return result
    }
  }

  function parseValue() {
    parseWhiteSpace()
    const value = 
      parseString() ||
      parseObject()
      
    parseWhiteSpace()
    return value
  }
  return parseValue()
}

测试一下

fakeParseJSON(obj) // {msg: "返回成功"}

parseArray

咱们继续丰富一下,在以前的 json 对象基础上增长数组类型

const obj = `
{
  "msg": "返回成功",
  "arr": ["a","b","c"]
}
`;

这里咱们有两个任务要处理,其一是处理新增的 , ,还有就是处理数组。

function fakeParseJSON(str) {
  let i = 0
  // ...
  // 处理逗号
  function parseComma() {
    if (str[i] !== ",") {
      throw new Error('Expected ",".');
    }
    i++;
  }
    
  // 处理对象
  function parseObject() {
    parseWhiteSpace()
    if(str[i] === '{') {
      // ...
      let initial = true
      while(str[i] !== '}') { 
        if(!initial) {
          parseComma() // 处理逗号
          parseWhiteSpace()
        }
        // ...
        initial = false
      }
      // ...
    }
  }
  // ...
}

其二是处理数组,咱们看看数组的流程图

和处理对象类型差很少,直接上代码

function fakeParseJSON(str) {
  let i = 0
  // ...
  // 处理数组
  function parseArray() {
    if(str[i] === "[") {
      i++
      parseWhiteSpace()
      
      const result = []
      let initial = true
      while(str[i] !== "]") {
        if(!initial) {
          parseComma()
          parseWhiteSpace()
        }
        const value = parseValue()
        result.push(value)
        initial = false
      }
      i++
      return result
    }
  }
  
  function parseValue() {
    parseWhiteSpace()
    const value = 
      parseString() ||
      parseObject() ||
      parseArray()
      
    parseWhiteSpace()
    return value
  }
  return parseValue()
}

parseNumber

处理 number 状况比较麻烦,咱们也是先看一下流程图

从图中能够看出,须要处理负数,小数以及指数等状况

function fakeParseJSON(str) {
  let i = 0
  // ...
  // 处理 number
  function parseNumber() {
    let start = i
    if (str[i] === "-") i++
    if (str[i] === "0") {
      i++
    } else if (str[i] >= "1" && str[i] <= "9") {
      i++
      while (str[i] >= "0" && str[i] <= "9") {
        i++;
      }
    }

    if (str[i] === ".") {
      i++
      while (str[i] >= "0" && str[i] <= "9") {
        i++
      }
    }
    if (str[i] === "e" || str[i] === "E") {
      i++
      if (str[i] === "-" || str[i] === "+") {
        i++
      }
      while (str[i] >= "0" && str[i] <= "9") {
        i++
      }
    }
    if (i > start) {
      return Number(str.slice(start, i));
    }
  }
  
  function parseValue() {
    parseWhiteSpace()
    const value = 
      parseString() ||
      parseObject() ||
      parseArray() ||
      parseNumber()

    parseWhiteSpace()
    return value
  }
  // 输出结果
  return parseValue()
}

其余状况

咱们还差布尔值以及 null 的状况

function fakeParseJSON(str) {
  let i = 0
  // ...
  // true, false and null
  function parseKeyword(name, value) {
    if (str.slice(i, i + name.length) === name) {
      i += name.length;
      return value;
    }
  }
  function parseValue() {
    parseWhiteSpace()
    const value = 
      parseString() ||
      parseObject() ||
      parseArray() ||
      parseNumber() ||
      parseKeyword("true", true) ||
      parseKeyword("false", false) ||
      parseKeyword("null", null)

    parseWhiteSpace()
    return value
  }
  // 输出结果
  return parseValue()
}

结语

至此,咱们大概实现了一个 JSON.parse 方法,固然还很不完善,好比字符串的处理以及容错处理。

字符串(_string_)是由双引号包围的任意数量Unicode字符的集合,使用反斜线转义。一个字符(character)即一个单独的字符串(character string)。

相关文章
相关标签/搜索