JSON stringify及parse方法实现数据深拷贝

目录
  • 引言
  • JSON.stringify
    • data
    • space
    • replacer
  • JSON.parse
    • 有限状态自动机
  • 解析流程
    • 数组的处理
  • 完整代码

引言

JSON 的 stringifyparse 两个方法在平时的工作中也很常用,如果没有一些特殊的类型,是实现数据深拷贝的一个原生方式。

下面就这两个方法的一个手动实现思路。

JSON.stringify

JSON.stringify 方法用于将 JavaScript 值转换为 JSON 字符串。该方法有三个参数:

  • data: 需要转换的数据
  • replacer:用于转换结果的对象或者数组,可以函数或者数组
  • space:文本添加缩进、空格和换行符,可以是数字或者字符串,数字的最大值是 10,字符串的最大长度是 10

下面的测试只用到这些类型:

number,string,function,object,array,null,undefined,map,set,weakmap,weakset

但是 JavaScript 数据的严格类型远远不止这几个。

data

首先我们用 JSON.stringify 来打印结果:

const testJson = {
  4: 3,
  n: 1,
  s: 's',
  f: () => { },
  null: null,
  unde: undefined,
  arr: [1, 's', null, undefined, () => { }],
  obj: {
    n: '1',
    s: 's'
  },
  map: new Map(),
  set: new Set([1, 2, 3]),
  wmap: new WeakMap(),
  wset: new WeakSet()
}
const raws = JSON.stringify(testJson)
// {
//  "4":3,"n":1,"s":"s","null":null,"arr":[1,"s",null,null,null],
//  "obj":{"n":"1","s":"s"},"map":{},"set":{},"wmap":{},"wset":{}
// }

根据上面的结果,我们可以发现对象内的 function, undefined 被剔除了,map, set 等都被动的转换成了空对象。而数组内的 functionundefined 被替换成了 null

所以我们可以根据上述规则写一个简单的 stringify 方法:

const stringify = (data: any) => {
  // 获取数据的严格类型
  const type = getType(data)
  let res = ''
  switch (type) {
    case 'Object':
      // 处理对象
      res = stringifyObject(data, indent, replacer, space)
      break
    case 'Array':
      // 处理数组
      res = stringifyArray(data, indent, space)
      break
    case 'Number':
      res = `${data}`
      break
    case 'String':
      res = `"${data}"`
      break
    case 'Null':
      res = 'null'
      break
    case 'Set':
    case 'WeakSet':
    case 'Map':
    case 'WeakMap':
      res = '{}'
      break
    default:
      return
  }
  return res
}

实现几个辅助函数:

// 获取严格类型
const getType = (data: any) => {
  return Object.prototype.toString.call(data).slice(8, -1)
}
// 处理对象方法
const stringifyObject = (data: Record<string, any>) => {
  const vals: string[] = []
  for (const key in data) {
    // 递归处理
    const val = stringify(data[key])
    // 如果值为 undefined,我们则需要跳过
    if (val !== undefined) {
      vals.push(`"${key}":${val}`)
    }
  }
  return `{${vals.join(',')}}`
}
// 处理数组方法
const stringifyArray = (data: any[]) => {
  const vals: any[] = []
  for (const val of data) {
    // 递归处理,如果返回 undefined 则替换为 null
    vals.push(stringify(val) || 'null')
  }
  return `[${vals.join(',')}]`
}

到这里就实现了 stringify 的简单版本。下面可以简单测试一下:

const raws = JSON.stringify(testJson)
const cuss = stringify(testJson)
console.log(raws === cuss) // true

后面还有两个参数,我们先实现第三个,第二个参数的作用等下在实现。

space

space 主要是用于添加空格、换行、缩进,但是只要 space 的值是合法的,换行符是默认加上一个的。所以我们要改下 stringify 的方法:

type Replacer = ((key: string, value: any) => any) | null | (string | number)[]
export const stringify = (data: any, replacer?: Replacer, space?: number | string, indent = 1) => {
  const type = getType(data)
  if (typeof space === 'number') {
    if (space <= 0) {
      space = undefined
    } else {
      space = Math.min(10, space)
    }
  } else if (typeof space === 'string') {
    space = space.substring(0, 10)
  } else if (space) {
    space = undefined
  }
  let res = ''
  switch (type) {
    case 'Object':
      res = stringifyObject(data, indent, replacer, space)
      break
    case 'Array':
      res = stringifyArray(data, indent, space)
      break
    // 省略部分代码
  }
  // 省略部分代码
}

对于 space 的不同非法的值,我们可以在控制台上进行一些简单的测试就可以得出,像 -1 这种其实是不生效的。

而我处理的是只能是数字和字符串,数字必须是 1 - 10,字符串的最长长度是 10 位,其余的都重置为 undefined。

因为像数组和对象的这种嵌套,缩进其实是要跟着动的,这里就新增了 indent 字段,初始为 1,后续递归就 + 1。

// 新增分隔符处理方法
const handleSeparator = (space: number | string, indent: number, prefix: string = '', suffix: string = '') => {
  let separator = prefix + '\n'
  if (typeof space === 'number') {
    separator += ' '.repeat(space).repeat(indent)
  } else {
    separator += space.repeat(indent)
  }
  return separator + suffix
}
// 对象方法修改
const stringifyObject = (data: Record<string, any>, indent: number, replacer?: Replacer, space?: number | string) => {
  const vals: string[] = []
  for (const key in data) {
    const val = stringify(data[key], null, space, indent + 1)
    if (val !== undefined) {
      vals.push(`"${key}":${space ? ' ' : ''}${val}`)
    }
  }
  // 新增 space 处理
  if (space) {
    const val = vals.join(handleSeparator(space, indent, ','))
    if (!val) {
      return '{}'
    }
    const front = handleSeparator(space, indent, '{')
    const back = handleSeparator(space, indent - 1, '', '}')
    return front + val + back
  }
  return `{${vals.join(',')}}`
}
// 数组处理方法
const stringifyArray = (data: any[], indent: number, space?: number | string) => {
  const vals: any[] = []
  for (const val of data) {
    vals.push(stringify(val) || 'null')
  }
  // 新增 space 处理
  if (space) {
    const front = handleSeparator(space, indent, '[')
    const val = vals.join(handleSeparator(space, indent, ','))
    const back = handleSeparator(space, indent - 1, '', ']')
    return front + val + back
  }
  return `[${vals.join(',')}]`
}

replacer

replacer 参数有两个类型,数组类型是用来过滤对象类型内的字段,只保留数组内的 key,而函数类型有点奇怪,有点不明白,函数的参数是 key 和 value,初始的 key 为空, value 就是当前的对象的值。

所以这里我们需要修改两处地方:

export const stringify = (data: any, replacer?: Replacer, space?: number | string, indent = 1) => {
  // 如果 replacer 为函数的话,直接返回函数运行后的值
  if (typeof replacer === 'function') {
    return replacer('', data)
  }
  const type = getType(data)
  // 省略部分代码
}
const stringifyObject = (data: Record<string, any>, indent: number, replacer?: Replacer, space?: number | string) => {
  const filter = getType(replacer) === 'Array' ? replacer : null
  const vals: string[] = []
  for (const key in data) {
    const val = stringify(data[key], null, space, indent + 1)
    if (
      val !== undefined &&
      (
        // 如果是数组,则当前的 key 必须是在 replacer 数组内
        !filter ||
        (filter as (string | number)[]).includes(key) ||
        (filter as (string | number)[]).includes(+key)
      )
    ) {
      vals.push(`"${key}":${space ? ' ' : ''}${val}`)
    }
  }
  // 省略部分代码
}

到这里, stringify 的方法差不多了。下面是完整代码:

type Replacer = ((key: string, value: any) => any) | null | (string | number)[]
const getType = (data: any) => {
  return Object.prototype.toString.call(data).slice(8, -1)
}
const handleSeparator = (space: number | string, indent: number, prefix: string = '', suffix: string = '') => {
  let separator = prefix + '\n'
  if (typeof space === 'number') {
    separator += ' '.repeat(space).repeat(indent)
  } else {
    separator += space.repeat(indent)
  }
  return separator + suffix
}
const stringifyObject = (data: Record<string, any>, indent: number, replacer?: Replacer, space?: number | string) => {
  const filter = getType(replacer) === 'Array' ? replacer : null
  const vals: string[] = []
  for (const key in data) {
    const val = stringify(data[key], null, space, indent + 1)
    if (
      val !== undefined &&
      (
        !filter ||
        (filter as (string | number)[]).includes(key) ||
        (filter as (string | number)[]).includes(+key)
      )
    ) {
      vals.push(`"${key}":${space ? ' ' : ''}${val}`)
    }
  }
  if (space) {
    const val = vals.join(handleSeparator(space, indent, ','))
    if (!val) {
      return '{}'
    }
    const front = handleSeparator(space, indent, '{')
    const back = handleSeparator(space, indent - 1, '', '}')
    return front + val + back
  }
  return `{${vals.join(',')}}`
}
const stringifyArray = (data: any[], indent: number, space?: number | string) => {
  const vals: any[] = []
  for (const val of data) {
    vals.push(stringify(val) || 'null')
  }
  if (space) {
    const front = handleSeparator(space, indent, '[')
    const val = vals.join(handleSeparator(space, indent, ','))
    const back = handleSeparator(space, indent - 1, '', ']')
    return front + val + back
  }
  return `[${vals.join(',')}]`
}
export const stringify = (data: any, replacer?: Replacer, space?: number | string, indent = 1) => {
  if (typeof replacer === 'function') {
    return replacer('', data)
  }
  const type = getType(data)
  if (typeof space === 'number') {
    if (space <= 0) {
      space = undefined
    } else {
      space = Math.min(10, space)
    }
  } else if (typeof space === 'string') {
    space = space.substring(0, 10)
  } else if (space) {
    space = undefined
  }
  let res = ''
  switch (type) {
    case 'Object':
      res = stringifyObject(data, indent, replacer, space)
      break
    case 'Array':
      res = stringifyArray(data, indent, space)
      break
    case 'Number':
      res = `${data}`
      break
    case 'String':
      res = `"${data}"`
      break
    case 'Null':
      res = 'null'
      break
    case 'Set':
    case 'WeakSet':
    case 'Map':
    case 'WeakMap':
      res = '{}'
      break
    default:
      return
  }
  return res
}

JSON.parse

stringify 方法的实现还是比较简单的,在一些笔试中还有可能会有相关需要实现的题。

JSON.parse 则是需要将合法的 json 字符串转换成对象,这里就需要用到一个概念:有限状态自动机

有限状态自动机

这里只做简单的介绍:有限状态机(Finite State Machine),是指任意时刻都处于有限状态集合中的某一状态。当其获得一个输入字符时,将从当前状态转换到另一个状态或者仍然保持当前状态。

可以结合当前 json 字符串的场景来简单理解一下:

我们有如下一个字符串:

const str = '{"4":3,"s":"s","null":null,"arr":[1,"s",null],"obj":{}}'

然后定义几个状态:

const State = {
  INIT: 'INIT',  // 初始状态
  OBJECTSTART: 'OBJECTSTART',  // 开始解析对象
  ARRAYSTART: 'ARRAYSTART',  // 开始解析数组
  OBJVALSTART: 'OBJVALSTART',  // 开始解析对象的属性与值
  OBJVALEND: 'OBJVALEND',  // 对象属性与值解析结束
  ARRVALSTART: 'ARRVALSTART' // 开始解析数组值
}

因为 json 字符串是非常规则的字符串,所以我们可以结合正则表达式来提取相关步骤的数据,在字符串中的 ' '\t\n\r 等也是可以的,所以在正则中需要考虑并且替换。

const parse = (data: string | number | null) => {
  if (typeof data === 'number' || data === null) {
    return data
  }
  // 将字符串转换为地址引用,方便后面字符串数据的消费
  const context = { data }
  // 具体解析方法
  return parseData(context)
}

然后定义几个辅助函数:

// 字符串的消费函数 - 就是截取已匹配完的数据,返回剩余字符串
const advance = (context: { data: string }, num: number) => {
  context.data = context.data.slice(num)
}
// 是否结束状态机
const isEnd = (ctx: { data: string }) => {
  // 如果没有数据了,则结束
  if (!ctx.data) {
    return false
  }
  const match = /^(}|\])[ \t\n\r]*/.exec(ctx.data)
  if (match) {
    if (
      match[1] === '}' && getType(res) !== 'Object' ||
      match[1] === ']' && getType(res) !== 'Array'
    ) {
      throw Error('解析错误')
    }
    advance(ctx, match[0].length)
    return false
  }
  return true
}
// 解析对象属性值
const parseObjValue = (context: { data: string }) => {
  const match = /^[ \n\t\r]*((".*?")|([0-9A-Za-z]*))[ \t\n\r]?/.exec(context.data)
  if (match) {
    advance(context, match[0].length)
    const valMatch = /^"(.*?)"$/.exec(match[1])
    if (valMatch) {
      return valMatch[1]
    }
    if (match[1] === 'null') {
      return null
    }
    if (isNaN(+match[1])) {
      throw Error('解析错误')
    }
    return Number(match[1])
  }
  new Error('解析错误')
}
// 解析数组值
const parseArrValue = (context: { data: string }) => {
  const refMatch = /^({|\][ \n\t\r]*)/.exec(context.data)
  // 开启新的状态机
  if (refMatch) {
    return parseData(context)
  }
  const match = /^((".*?")|([0-9a-zA-Z]*))[ \n\t\r]*[,]?[ \n\t\r]*/.exec(context.data)
  if (match) {
    advance(context, match[0].length)
    const valMatch = /^"(.*?)"$/.exec(match[1])
    if (valMatch) {
      return valMatch[1]
    }
    if (match[1] === 'null') {
      return null
    }
    if (isNaN(+match[1])) {
      throw Error('解析错误')
    }
    return Number(match[1])
  }
  throw Error('解析错误')
}

在上面定义状态的时候,解析对象、数组和数组值的时候只有开始状态,而没有结束状态。只是结束状态统一放入 isEnd 函数中,。

解析流程

下面开始定义 parseData 函数:

第一步

const parseData = (ctx: { data: string }) => {
  let res: any = ''
  let currentState = State.INIT
  while (isEnd(ctx)) {
    switch (CurrentState) {
      case State.INIT:
        {
          const match = /^[ \t\n\r]*/.exec(ctx.data)
          if (match?.[0].length) {
            advance(ctx, match[0].length)
          }
          if (ctx.data[0] === '{') {
            res = {}
            currentState = State.OBJECTSTART
          } else if (ctx.data[0] === '[') {
            res = []
            currentState = State.ARRAYSTART
          } else {
            res = parseObjValue(ctx)
          }
        }
        break
      case State.OBJECTSTART:
        break
      case State.OBJVALSTART:
        break
      case State.OBJVALEND:
        break
      case State.ARRAYSTART:
        break
      case State.ARRVALSTART:
        break
      // no default
    }
  }
  return res
}

INIT 中,先去掉前面的空格、换行等字符,示例:

const str1 = ' \t\n\r{"4":3,"s":"s","null":null,"arr":[1,"s",null],"obj":{}}'
const str2 = '{"4":3,"s":"s","null":null,"arr":[1,"s",null],"obj":{}}'

然后再判读第一个字符是什么:

  • 如果是 {,则将状态转移到 OBJECTSTART,将 res 赋值一个空对象
  • 如果是 [,则将状态转移到 ARRAYSTART,将 res 赋值一个空数组
  • 如果都不是,则就是一个值,可以用对象解析属性值的方法来解析,判读是否是合法的字符串

所以这里的状态转移到了对象解析 OBJECTSTART

第二步

const parseData = (ctx: { data: string }) => {
  let res: any = ''
  let currentState = State.INIT
  while (isEnd(ctx)) {
    switch (CurrentState) {
      case State.INIT:
        // 省略部分代码
        break
      case State.OBJECTSTART:
        {
          const match = /^{[ \t\n\r]*/.exec(ctx.data)
          if (match) {
            advance(ctx, match[0].length)
            currentState = State.OBJVALSTART
          }
        }
        break
      case State.OBJVALSTART:
        break
      case State.OBJVALEND:
        break
      case State.ARRAYSTART:
        break
      case State.ARRVALSTART:
        break
      // no default
    }
  }
  return res
}

OBJECTSTART 中,消费掉 '{',将状态转移到 OBJVALSTART, 剩余字符数据:

const str = '"4":3,"s":"s","null":null,"arr":[1,"s",null],"obj":{}}'

第三步

const parseData = (ctx: { data: string }) => {
  let res: any = ''
  let currentState = State.INIT
  while (isEnd(ctx)) {
    switch (CurrentState) {
      case State.INIT:
        // 省略部分代码
        break
      case State.OBJECTSTART:
        // 省略部分代码
        break
      case State.OBJVALSTART:
        {
          const match = /^"(.*?)"[ \n\t\r]*:[ \n\t\r]*/.exec(ctx.data)
          if (match) {
            advance(ctx, match[0].length)
            if (ctx.data[0] === '{' || ctx.data[0] === '[') {
              res[match[1]] = parseData(ctx)
            } else {
              res[match[1]] = parseObjValue(ctx)
            }
            currentState = State.OBJVALEND
          }
        }
        break
      case State.OBJVALEND:
        break
      case State.ARRAYSTART:
        break
      case State.ARRVALSTART:
        break
      // no default
    }
  }
  return res
}

先获取 key: 等数组并消费,剩余字符数据:

const str = '3,"s":"s","null":null,"arr":[1,"s",null],"obj":{}}'

先判读后续字符的第一个字符是什么:

  • 如果是 { 或者 [,则开启一个新的状态机
  • 否则直接用 parseObjValue 解析值

最后将状态转移至 OBJVALEND

第四步

const parseData = (ctx: { data: string }) => {
  let res: any = ''
  let currentState = State.INIT
  while (isEnd(ctx)) {
    switch (CurrentState) {
      case State.INIT:
        // 省略部分代码
        break
      case State.OBJECTSTART:
        // 省略部分代码
        break
      case State.OBJVALSTART:
        // 省略部分代码
        break
      case State.OBJVALEND:
        {
          const match = /^[ \t\n\r]*([,}\]])[ \t\n\r]*/.exec(ctx.data)
          if (match) {
            if (match[1] === ',') {
              currentState = State.OBJVALSTART
            }
            advance(ctx, match[0].length)
          }
        }
        break
      case State.ARRAYSTART:
        break
      case State.ARRVALSTART:
        break
      // no default
    }
  }
  return res
}

如果后面匹配出来的字符是 ,,则表示后续还有其它的对象属性,我们需要将状态重新转移到 OBJVALSTART, 如果是其它的 } 或者 ],则会在此次消费完毕,然后在 isEnd 中会退出状态机。

后续剩余字符的变化会依照上数状态的变化而进行字符消费:

const str = '3,"s":"s","null":null,"arr":[1,"s",null],"obj":{}}'
// 1
const str = ',"s":"s","null":null,"arr":[1,"s",null],"obj":{}}'
// 2
const str = '"s":"s","null":null,"arr":[1,"s",null],"obj":{}}'
// 省略 s 和 null
// 3 开启新的状态机
const str = '[1,"s",null],"obj":{}}'
// 4 结束状态机
const str = '],"obj":{}}'
// 5 开启新的状态机
const str = '{}}'
// 6 结束状态机
const str = '}}'
// 7 结束状态机
const str = '}'

数组的处理

const parseData = (ctx: { data: string }) =&gt; {
  let res: any = ''
  let currentState = State.INIT
  while (isEnd(ctx)) {
    switch (CurrentState) {
      case State.INIT:
        // 省略部分代码
        break
      case State.OBJECTSTART:
        // 省略部分代码
        break
      case State.OBJVALSTART:
        // 省略部分代码
        break
      case State.OBJVALEND:
        // 省略部分代码
        break
      case State.ARRAYSTART:
        {
          const match = /^\[[ \t\n\r]*/.exec(ctx.data)
          if (match) {
            advance(ctx, match[0].length)
            currentState = State.ARRVALSTART
          }
        }
        break
      case State.ARRVALSTART:
        res.push(parseArrValue(ctx))
        break
      // no default
    }
  }
  return res
}

如果第一个字符为 [,则会开启新的状态机,状态也会转换为 ARRAYSTART,然后在 ARRAYSTART 状态内进行数组值的转换。

到这里整个 JSON.parse 的实现思路差不多,但是上述的流程应该有没考虑到的地方,但是大体差不多,只是边界的处理问题。测试示例:

// 数据使用上面的 testJson
const raws = JSON.stringify(testJson)
const rawp = JSON.parse(raws)
const cusp = parse(raws)
console.log(raws, 'JSON.stringify')
console.log(rawp, 'JSON.parse')
console.log(cusp, 'parse')

结果:

完整代码

const State = {
  INIT: 'INIT',
  OBJECTSTART: 'OBJECTSTART',
  ARRAYSTART: 'ARRAYSTART',
  OBJVALSTART: 'OBJVALSTART',
  OBJVALEND: 'OBJVALEND',
  ARRVALSTART: 'ARRVALSTART'
}
const isEnd = (ctx: { data: string }, res: any) => {
  if (!ctx.data) {
    return false
  }
  const match = /^(}|\])[ \t\n\r]*/.exec(ctx.data)
  if (match) {
    if (
      match[1] === '}' && getType(res) !== 'Object' ||
      match[1] === ']' && getType(res) !== 'Array'
    ) {
      throw Error('解析错误')
    }
    advance(ctx, match[0].length)
    return false
  }
  return true
}
const advance = (context: { data: string }, num: number) => {
  context.data = context.data.slice(num)
}
const parseObjValue = (context: { data: string }) => {
  const match = /^[ \n\t\r]*((".*?")|([0-9A-Za-z]*))[ \t\n\r]?/.exec(context.data)
  if (match) {
    advance(context, match[0].length)
    const valMatch = /^"(.*?)"$/.exec(match[1])
    if (valMatch) {
      return valMatch[1]
    }
    if (match[1] === 'null') {
      return null
    }
    if (isNaN(+match[1])) {
      throw Error('解析错误')
    }
    return Number(match[1])
  }
  new Error('解析错误')
}
const parseArrValue = (context: { data: string }) => {
  const refMatch = /^({|\][ \n\t\r]*)/.exec(context.data)
  if (refMatch) {
    return parseData(context)
  }
  const match = /^((".*?")|([0-9a-zA-Z]*))[ \n\t\r]*[,]?[ \n\t\r]*/.exec(context.data)
  if (match) {
    advance(context, match[0].length)
    const valMatch = /^"(.*?)"$/.exec(match[1])
    if (valMatch) {
      return valMatch[1]
    }
    if (match[1] === 'null') {
      return null
    }
    if (isNaN(+match[1])) {
      throw Error('解析错误')
    }
    return Number(match[1])
  }
  throw Error('解析错误')
}
const parseData = (ctx: { data: string }) => {
  let res: any = ''
  let currentState = State.INIT
  while (isEnd(ctx, res)) {
    switch (currentState) {
      case State.INIT:
        {
          const match = /^[ \t\n\r]*/.exec(ctx.data)
          if (match?.[0].length) {
            advance(ctx, match[0].length)
          }
          if (ctx.data[0] === '{') {
            res = {}
            currentState = State.OBJECTSTART
          } else if (ctx.data[0] === '[') {
            res = []
            currentState = State.ARRAYSTART
          } else {
            res = parseObjValue(ctx)
          }
        }
        break
      case State.OBJECTSTART:
        {
          const match = /^{[ \t\n\r]*/.exec(ctx.data)
          if (match) {
            advance(ctx, match[0].length)
            currentState = State.OBJVALSTART
          }
        }
        break
      case State.OBJVALSTART:
        {
          const match = /^"(.*?)"[ \n\t\r]*:[ \n\t\r]*/.exec(ctx.data)
          if (match) {
            advance(ctx, match[0].length)
            if (ctx.data[0] === '{' || ctx.data[0] === '[') {
              res[match[1]] = parseData(ctx)
            } else {
              res[match[1]] = parseObjValue(ctx)
            }
            currentState = State.OBJVALEND
          }
        }
        break
      case State.OBJVALEND:
        {
          const match = /^[ \t\n\r]*([,}\]])[ \t\n\r]*/.exec(ctx.data)
          if (match) {
            if (match[1] === ',') {
              currentState = State.OBJVALSTART
            }
            advance(ctx, match[0].length)
          }
        }
        break
      case State.ARRAYSTART:
        {
          const match = /^\[[ \t\n\r]*/.exec(ctx.data)
          if (match) {
            advance(ctx, match[0].length)
            currentState = State.ARRVALSTART
          }
        }
        break
      case State.ARRVALSTART:
        res.push(parseArrValue(ctx))
        break
      // no default
    }
  }
  return res
}
export const parse = (data: string | number | null) => {
  if (typeof data === 'number' || data === null) {
    return data
  }
  const context = { data }
  return parseData(context)
}

以上就是JSON stringify及parse方法实现数据深拷贝的详细内容,更多关于JSON数据深拷贝stringify及parse的资料请关注我们其它相关文章!

(0)

相关推荐

  • 详解JSON.parse和JSON.stringify用法

    JSON格式,(简写JavaScript Object Notation),是一种用于数据交换的文本格式,书写简单.基于JavaScript原生语法,能够序列化对象.数组.数值.字符串.布尔值和 null. 在ES5中,增加了一个JSON对象,专门用来处理JSON格式的数据.JSON是一个对象,但只有两个方法:parse 和 stringify,不能作为构造函数,也无属性. typeof JSON === 'object' JSON.parse JSON.parse() 用来解析JSON字符串,

  • JSON中key动态设置及JSON.parse和JSON.stringify()的区别

    本文给大家介绍JSON中key动态设置及JSON.parse和JSON.stringify()的区别讲解,具体详情如下所示: var user_info_json = []; user_info_json[user_info_json.length] = eval('('+ '{"'+ id +'": "'+ value +'"}' +')'); JSON.stringify(user_info_json) 先转成字符串,然后使用eval()格式化,然后在把json

  • JS使用JSON.parse(),JSON.stringify()实现对对象的深拷贝功能分析

    本文实例讲述了JS使用JSON.parse(),JSON.stringify()实现对对象的深拷贝功能.分享给大家供大家参考,具体如下: 根据不包含引用对象的普通数组深拷贝得到启发,不拷贝引用对象,拷贝一个字符串会新辟一个新的存储地址,这样就切断了引用对象的指针联系. 测试例子: var test={ a:"ss", b:"dd", c:[ {dd:"css",ee:"cdd"}, {mm:"ff",nn:

  • JavaScript 中 JSON.parse 函数 和 JSON.stringify 函数

    1. JSON.parse 函数: 使用 JSON.parse 可将 JSON 字符串转换成对象. <!doctype html> <html> <head> <meta charset="UTF-8"> <title>Document</title> <script type="text/javascript"> var jsontext = '{"Name":

  • 详解关于JSON.parse()和JSON.stringify()的性能小测试

    JSON.parse(JSON.stringify(obj))我们一般用来深拷贝,其过程说白了,就是利用 JSON.stringify 将js对象序列化(JSON字符串),再使用JSON.parse来反序列化(还原)js对象.至于这行代码为什么能实现深拷贝,以及它有什么局限性等等,不是本文要介绍的.本文要探究的是,这行代码的执行效率如何?如果随意使用会不会造成一些问题? 先上两个js性能测试的依赖函数 /** * 一个简单的断言函数 * @param value {Boolean} 断言条件 *

  • json.stringify()与json.parse()的区别以及用处

    一.JSON.stringify()和JSON.parse() 区别 我们都用过JSON.stringify()和JSON.parse() ,从名字上就能知道 JSON.stringify()的作用是将JavaScript对象转换为JSON 字符串 JSON.parse()可以将JSON字符串转为一个对象. 通俗易懂版: JSON.stringify() 将对象a转化成字符串s; JSON.parse() 将字符串s转化成对象a; 简单点说,它们的作用是相对的,我用JSON.stringify(

  • JSON stringify及parse方法实现数据深拷贝

    目录 引言 JSON.stringify data space replacer JSON.parse 有限状态自动机 解析流程 数组的处理 完整代码 引言 JSON 的 stringify 和 parse 两个方法在平时的工作中也很常用,如果没有一些特殊的类型,是实现数据深拷贝的一个原生方式. 下面就这两个方法的一个手动实现思路. JSON.stringify JSON.stringify 方法用于将 JavaScript 值转换为 JSON 字符串.该方法有三个参数: data: 需要转换的

  • 浅谈JSON.stringify()和JOSN.parse()方法的不同

    今天终于把JSON.stringify()跟JSON.parse()这两个方法给搞清楚了,下面小编在此记录下! JSON.tringify():把一个json数据转化成JSON string JSON.stringify({uno:1,dos:2},null,'\t') "{ "uno": 1, "dos": 2 }" JSON.stringfy({uno:1,dos:2}) JSON.stringify({uno:1,dos:2}) "

  • 谈谈JSON对象和字符串之间的相互转换JSON.stringify(obj)和JSON.parse(string)

    在Firefox,chrome,opera,safari,ie9,ie8等高级浏览器直接可以用JSON对象的stringify()和parse()方法. JSON.stringify(obj)将JSON转为字符串.JSON.parse(string)将字符串转为JSON格式: var a={"name":"tom","sex":"男","age":"24"}; var aToStr =

  • JSON的parse()方法介绍

    parse()方法的介绍: 在接收服务器数据时一般是字符串. 我们可以使用 JSON.parse() 方法将数据转换为 JavaScript 对象. 语法 JSON.parse(text[, reviver]) 参数说明: text:必需, 一个有效的 JSON 字符串. reviver: 可选,一个转换结果的函数, 将为对象的每个成员调用此函数. 下面我们来看一个实例: <p id="demo"></p> <script> var obj = JS

  • jQuery解决IE6、7、8不能使用 JSON.stringify 函数的问题

    JSON 对象是在 ECMAScript 第 5 版中实现的,此版于 2009 年 12 月发布:IE6 IE7 与 IE8(Q) (IE8(Q) 相当于 IE 5.5) 发布时间比较早,没有在其 Javascript 引擎中实现该对象. 好在我们可以使用 json2.js 作为兼容. 地址:https://github.com/douglascrockford/JSON-js 这个JS中的函数将JSON对象转换成JSON字符串,解决 IE6.7.8不能使用 JSON.stringify 函数的

  • JavaScript对象与JSON格式的转换及JSON.stringify和JSON.parse的使用方法

    目录 JSON处理 JSON.stringify stringify的限制 排除和替换 映射函数 格式化使用的空格数量 自定义toJSON方法 JSON.parse 使用reviver 总结 JSON处理 JSON(JavaScript Object Notation)是JavaScript表达值和对象的通用数据格式,其本质就是符合一定规范的字符串.由于JSON的优良特性,非常容易和其他语言进行数据交换,尤其在前后端交互方面.即使我们前端使用JavaScript,后端使用Java/PHP/Pyt

随机推荐