利用原生 js 模拟出 JSON.parse 和 JSON.stringify

最近利用原生 javascript 模仿出原生的 JSON.parse 和 JSON.stringify 的效果

(function (win) {
  const MOCK_JSON = {
    // 记录结构体的数量(结构体是指字符串格式的[]和{})
    structure_len: 0,
    // 记录结构体的结构对象
    map_data: {},
    structure_key: '$',
    init() {
      this.structure_len = 0
      this.map_data = {}
      this.structure_key = '$'
    },
    parse(str) {
      this.init()
      // this.inspectionFormat()        
      let reg = /\\/g
      if (reg.test(str)) {
        if (str[0] == '"') {
          str = str.slice(1, str.length - 1)
        }
        return str.replace(reg, '')
      }
      str = str.replace(/\[ /g, '[')
      str = str.replace(/\s,/g, ',')
      str = str.replace(/,\s/g, ',')
      str = str.replace(/\s:/g, ':')
      str = str.replace(/:\s/g, ':')
      // 处理结构体 key
      let $arr = str.match(/\$+/g)
      let $len = $arr[0].length
      for (let i = 1; i < $arr.length; i++) {
        if ($len < $arr[i].length) {
          $len = $arr[i].length
        }
      }
      let $key = this.structure_key
      for (let i = 0; i < $len; i++) {
        this.structure_key += $key
      }
      return this.handleData(str, 0)
    },
    inspectionFormat(obj) {
      let regObject = /{"?.+[^"]:.+}|{[^"].+"?:.+}|{[^"]|{":|{".*"}|{".*":.{0}}|{".*"[^:].{0}}|,}| "/g
      let regArray = /\[,.*|.*,\]/g
      if (regObject.test(obj)) {
        throw new Error('{} 数组格式有误')
      }
      if (regArray.test(obj)) {
        throw new Error('[] 数组格式有误')
      }
    },
    changeData(data) {
      --this.structure_len
      if (this.callType(data) == 'array') {
        let array = []
        let len = data.length
        for (let i = 0; i < len; i++) {
          let elem = data[i]
          if (this.callType(elem) == 'string' && elem.indexOf(this.structure_key) > -1) {
            array.push(this.changeData(this.map_data[elem]))
          } else {
            array.push(this.handleValue(elem))
          }
        }
        if (!this.structure_len) {
          this.init()
        }
        return array
      } else if (this.callType(data) == 'object') {
        let object = {}
        for (let attr in data) {
          let value = data[attr]
          if (this.callType(value) == 'string' && value.indexOf(this.structure_key) > -1) {
            object[attr] = this.changeData(this.map_data[value])
          } else {
            object[attr] = value
          }
        }
        if (!this.structure_len) {
          this.init()
        }
        return object
      }
    },
    handleData(obj, idx) {
      let left_brace = obj.lastIndexOf('{'),
        right_brace = obj.slice(left_brace).indexOf('}'),
        left_bracket = obj.lastIndexOf('['),
        right_bracket = obj.slice(left_bracket).indexOf(']')
      if (left_brace == -1 && left_bracket == -1) {
        return this.changeData(this.map_data[this.structure_key + (this.structure_len - 1)])
      }
      // 最小结构体为 {}
      if (left_brace > left_bracket) {
        this.map_data[`${this.structure_key}${idx}`] = this.handleObject(obj.slice(left_brace, left_brace + right_brace + 1))
          ++this.structure_len
        let left_surplus = obj.slice(0, left_brace)
        let right_surplus = obj.slice(left_brace + right_brace + 1)
        return this.handleData(`${left_surplus}${this.structure_key}${idx}${right_surplus}`, ++idx)
      } else if (left_brace < left_bracket) {
        // 最小结构体为 []
        this.map_data[`${this.structure_key}${idx}`] = this.handleArray(obj.slice(left_bracket, left_bracket + right_bracket + 1))
          ++this.structure_len
        let left_surplus = obj.slice(0, left_bracket)
        let right_surplus = obj.slice(left_bracket + right_bracket + 1)
        return this.handleData(`${left_surplus}${this.structure_key}${idx}${right_surplus}`, ++idx)
      } else {
        // 最外层为 {}
        if (obj[0] == '{') {
          this.map_data[`${this.structure_key}${idx}`] = this.handleObject(obj)
            ++this.structure_len
        }
        // 最外层为 []
        if (obj[0] == '[') {
          this.map_data[`${this.structure_key}${idx}`] = this.handleArray(obj)
            ++this.structure_len
        }
      }
    },
    // 对象字符串格式化为对象
    handleObject(obj) {
      let newObj = {}
      // 空对象
      if (obj.length == 2) {
        return newObj
      }
      // 检查对象格式
      let regObject = /{"?.+[^"]:.+}|{[^"].+"?:.+}|{[^"]|{":|{".*"[^:]}|{".*":.{0}}|{".*"[^:].{0}}|,}/g
      if (regObject.test(obj)) {
        throw new Error(obj + ' 对象结构有误')
      }
      // 去除对象左右{}符号
      obj = obj.slice(1, obj.length - 1)
      // 先根据逗号分割
      let obj_arr = obj.split(',')
      obj_arr.forEach((elem) => {
        // 再根据冒号分割 key 和 value
        let obj_data = elem.split(':')
        for (let o = 0; o < obj_data.length; o++) {
          let key = this.handleKey(obj_data[o])
          let value = this.handleValue(obj_data[++o])
          newObj[key] = value
        }
      })
      return newObj
    },
    // 数组字符串格式化为数组
    handleArray(arr) {
      let newArr = []
      // 空数组
      if (arr.length == 2) {
        return newArr
      }
      // 检查数组格式
      let reg = /\[,.*|.*,\]/g
      if (reg.test(arr)) {
        throw new Error(arr + ' 数组格式有误')
      }
      return arr.slice(1, arr.length - 1).split(',').map((elem) => this.handleValue(elem))
    },
    handleKey(key) {
      return key.replace(/"/g, '')
    },
    handleValue(value) {
      // 空字符串
      if (value.length == 0) {
        return value
      }
      // 存储字符串长度
      let str_len = 0
      if (this.callType(value) == 'string') {
        value = value.replace(/"/g, '')
        str_len = value.length
      }
      // 字符串中无数字
      if (isNaN(parseFloat(value))) {
        return value
      } else {
        // 字符串中有数字(字符串数字化后是否与原先字符串一致)
        let temp = '' + parseFloat(value)
        // 不一致(例如:parseFloat('123abc'))
        if (temp.length != str_len) {
          return value
        } else {
          // 一致(例如:parseFloat('123'))
          return parseFloat(value)
        }
      }
    },
    // 分辨类型
    callType(obj) {
      let map = {
        '[object Object]': 'object',
        '[object Array]': 'array',
        '[object Number]': 'number',
        '[object Null]': 'null',
        '[object Boolean]': 'boolean',
        '[object Symbol]': 'symbol',
        '[object Undefined]': 'undefined',
        '[object Function]': 'function',
        '[object RegExp]': 'regexp',
        '[object String]': 'string',
        '[object Date]': 'date',
        '[object Math]': 'math',
      }
      return map[Object.prototype.toString.call(obj)]
    },
    // json对象字符串化
    stringify(obj) {
      switch (this.callType(obj)) {
        case 'object':
          let listObj = []
          for (let attr in obj) {
            listObj.push(`"${attr}":${this.stringify(obj[attr])}`)
          }
          return `{${listObj.join(',')}}`
        case 'array':
          let listArr = []
          obj.forEach((elem) => {
            listArr.push(this.stringify(elem))
          })
          return `[${listArr.join(',')}]`
        case 'number':
        case 'null':
        case 'boolean':
          return String(obj)
        case 'symbol':
        case 'undefined':
        case 'function':
        case 'regExp':
          return undefined
        case 'string':
          return `"${obj.replace(/"/g, '\\"')}"`
        case 'date':
          return `"${obj.toISOString()}"`
        case 'math':
          return `{}`
      }
    }
  }
  win.MOCK_JSON = MOCK_JSON
})(window)

代码中也有简单的注释

posted @ 2020-12-21 22:42  Tsingtree  阅读(295)  评论(0编辑  收藏  举报