Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

重学js —— 结构化数据之JSON对象 #126

Open
lizhongzhen11 opened this issue Jul 2, 2020 · 0 comments
Open

重学js —— 结构化数据之JSON对象 #126

lizhongzhen11 opened this issue Jul 2, 2020 · 0 comments
Labels
js基础 Good for newcomers 重学js 重学js系列 规范+MDN

Comments

@lizhongzhen11
Copy link
Owner

lizhongzhen11 commented Jul 2, 2020

JSON 对象

JSON全称 JavaScript Object Notation

  • MDN
  • 即固有对象 %JSON%
  • 全局对象 "JSON" 属性的初始值
  • 属于 普通对象
  • 包含两个函数:parsestringify,用于解析和构造 JSON 文本。
  • [[Prototype]] 内置插槽,其值为 %Object.prototype%
  • 没有 [[Construct]] 内置方法;不能作为构造器
  • 没有 [[Call]] 内置方法;不能被调用

ECMA-404 中定义了JSON数据交换格式。

JSON.parse ( text [ , reviver ] )

  • MDN
  • 该函数为固有对象 %JSONParse%

JSON格式使用与ECMAScript字面量,数组初始化器和对象初始化器相似的语法表示字面量,数组和对象。解析后,JSON对象被实现为ECMAScript对象。JSON数组被实现为ECMAScript数组。JSON字符串,数字,布尔和 null 被实现为ECMAScript的字符串,数字,布尔和 null

参数 reviver 可选,它是个接收参数 keyvalue 的函数。

  1. 定义 jsonString? ToString(text)
  2. 解析 ! UTF16DecodeString(jsonString) 为ECMA-404指定的JSON文本。如果它不是合法的JSON文本 ,抛 SyntaxError 异常
  3. 定义 scriptString"(", jsonString, ");" 字符串拼接
  4. 定义 completion! UTF16DecodeString(scriptString) 解析和求值结果,仿佛它是ECMAScript 脚本 的源文本一样。求值期间不得使用 B.3.1 中定义的扩展PropertyDefinitionEvaluation语义。
  5. 定义 unfilteredcompletion.[[Value]]
  6. 断言:unfiltered 要么是 StringNumberBooleanNull,要么是由 数组字面量对象字面量 定义的 Object
  7. 如果 reviver 可调用,
    1. 定义 rootOrdinaryObjectCreate(%Object.prototype%)
    2. 定义 rootName 为空字符串
    3. 执行 ! CreateDataPropertyOrThrow(root, rootName, unfiltered)
    4. 返回 ? InternalizeJSONProperty(root, rootName, reviver)
  8. 否则,
    1. 返回 unfiltered

JSON.stringify ( value [ , replacer [ , space ] ] )

返回UTF-16编码JSON格式的字符串或 undefined

  1. 定义 stack 为新的空 List
  2. 定义 indent 为空字符串
  3. 定义 PropertyListReplacerFunctionundefined
  4. 如果 replacer 是对象,
    1. 如果 replacer 可调用,
      1. ReplacerFunction 设置为 replacer
    2. 否则,
      1. 定义 isArray? IsArray(replacer)
      2. 如果 isArraytrue
        1. PropertyList 设置为空 List
        2. 定义 len? LengthOfArrayLike(replacer)
        3. 定义 k 为0
        4. k < len 时重复以下步骤,
          1. 定义 v? Get(replacer, ! ToString(k))
          2. 定义 itemundefined
          3. 如果 vString,将 item 设置为 v
          4. 否则如果 vNumber,将 item 设置为 ! ToString(v)
          5. 如果 vObject
            1. 如果 v[[StringData]][[NumberData]] 内置插槽,将 item 设置为 ? ToString(v)
          6. 如果 item 不是 undefineditem 也不在 PropertyList 中,
            1. item 添加到 PropertyList 尾部
          7. k 设置为 k + 1
  5. 如果 spaceObject
    1. 如果 space[[NumberData]] 内置插槽,
      1. space 设置为 ? ToNumber(space)
    2. 如果 space[[StringData]] 内置插槽,
      1. space 设置为 ? ToString(space)
  6. 如果 spaceNumber
    1. space 设置为 min(10, ! ToInteger(space))
    2. 如果 space < 1,定义 gap 为空字符串;否则定义 gap 为包含代码单元0x0020(SPACE)的空格出现的 String 值。
  7. 如果 spaceString
    1. 如果 space 长度小于或等于10,定义 gapspace;否则定义 gap 为由空格的前10个代码单元组成的 String 值。
  8. 否则,
    1. 定义 gap 为空字符串
  9. 定义 wrapperOrdinaryObjectCreate(%Object.prototype%)
  10. 执行 ! CreateDataPropertyOrThrow(wrapper, 空字符串, value)
  11. 定义 stateRecord { [[ReplacerFunction]]: ReplacerFunction, [[Stack]]: stack, [[Indent]]: indent, [[Gap]]: gap, [[PropertyList]]: PropertyList }
  12. 返回 ? SerializeJSONProperty(state, 空字符串, wrapper)

该函数即 %JSONStringify% 固有对象。

关于深拷贝

开发中经常图省事直接用 JSON.parse(JSON.stringify(目标)) 来进行深拷贝。一般都是懒,不想引入第三方工具类库,又不想自己写,但是用这个需要牢记有以下坑点:

// 1. 参数不能是 undefined
JSON.parse(JSON.stringify(undefined)) // 报错

// 2. 参数不能是 BigInt 类型
JSON.parse(JSON.stringify(1n)) // 报错

// 3. 参数不能是 Symbol 类型
JSON.parse(JSON.stringify(Symbol())) // 报错

// 4. 参数不能是 Function
JSON.parse(JSON.stringify(() => {})) // 报错

// 5. NaN
JSON.parse(JSON.stringify(NaN)) // null

// 6. Infinity
JSON.parse(JSON.stringify(Infinity)) // null

// 7. -Infinity
JSON.parse(JSON.stringify(-Infinity)) // null

// 8. Date
typeof (JSON.parse(JSON.stringify(new Date))) // string
typeof (new Date) // object

// 9. 不要有循环引用,对所有深拷贝来说,循环引用都是非常不好的

能使用该方法进行深拷贝的只能是 NullStringBoolean,普通Number数字,普通Object 以及 Array。如果你确定业务中的参数类型只在这些范围内,那你用吧,否则乖乖用 lodash 等第三方库进行深拷贝,当然,手写更好了

2020-07-22 补充

早上逛知乎发现个有意思的问题:https://www.zhihu.com/question/408223837/answer/1352766043

我立马想到的是 JSON.parse

JSON.parse("[ {a:1 , d:3} , {b:2} , {c:3} ]") // 报错

报错是由于该字符串内的 {a:1 , d:3} 以及 {b:2} 和 {c:3} 并不是符合规范的 JSON 对象(键名缺少双引号)。这就有点麻烦了。不过提问者也给出了解决方法,利用 String.prototype.replace 来替换下,这需要对该API非常熟悉的人才能想到,我还不行。这个正则我不会写。。。

@lizhongzhen11 lizhongzhen11 added js基础 Good for newcomers 重学js 重学js系列 规范+MDN labels Jul 2, 2020
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
js基础 Good for newcomers 重学js 重学js系列 规范+MDN
Projects
None yet
Development

No branches or pull requests

1 participant