手把手用代碼教你實現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)。

相關文章
相關標籤/搜索