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

64.深度使用 JSON.stringify() #69

Open
ccforward opened this issue Jul 26, 2017 · 14 comments
Open

64.深度使用 JSON.stringify() #69

ccforward opened this issue Jul 26, 2017 · 14 comments

Comments

@ccforward
Copy link
Owner

ccforward commented Jul 26, 2017

深度使用 JSON.stringify()

按照 JSON 的规范,使用 JSON.stringify() 做对象序列化时,如果一个属性为函数,那这个属性就会被忽略。

const data1 = {
  a: 'aaa',
  fn: function() {
    return true
  }
}
JSON.stringify(data)

// 结果是  "{"a":"aaa"}"

还有一种情况,一个属性的值为 undefined

const data2 = {
  a: 'abc',
  b: undefined
}
JSON.stringify(data2)

// 结果是  "{"a":"abc"}"

如果属性为 null 则可以正常序列化这个属性:

const data3 = {
  a: 'abc',
  b: null
}
JSON.stringify(data3)

// 结果是  "{"a":"abc","b":null}"

因为 null 可表示已经赋值,而 undefined 表示未定义、未赋值,所以执行 JSON.stringify 不会处理。

stringify 函数

stringify 函数的定义为 JSON.stringify(value [, replacer [, space]])

后面还带有我不常用两个可选参数 replacer 和 space

value 参数不多解释,replacer 其实就是一个自定义函数,可以改变 JSON.stringify 的行为,space 就是格式化输出,最大值为 10,非整数时取值为 1

stringify 输出 Function

本质上无论怎么改,stringify 还是不会输出 Function,但是 Function 可以调用 toString() 方法的,所以思路就很明了了。

const data1 = {
  a: 'aaa',
  fn: function() {
    return true
  }
}

const replace = function(k ,v) {
  if(typeof v === 'function') {
    return Function.prototype.toString.call(v)
  }
  return v
}

JSON.stringify(data1, replace)

// 结果  "{"a":"aaa","fn":"function () {\n    return true\n  }"}"

同理可证 undefined 也能输出了

const replace = function(k ,v) {
  if(v === undefined){
    return 'undefined'
  }
  return v
}

stringify 格式化输出

JSON.stringify 的第三个参数很简单,相当于我们编辑器的 tab_size

const data4 = {
  a: 'abc',
  b: null,
  c: {
    x: 'xxx',
    y: 'yyy',
    z: 2046
  },
  d: 9527
}

JSON.stringify(data4, null, 2);

// 输出结果

/*
"{
  "a": "abc",
  "b": null,
  "c": {
    "x": "xxx",
    "y": "yyy",
    "z": 2046
  },
  "d": 9527
}"
*/

toJSON 方法

toJSON 是个覆盖函数,尽量少用,看了代码就懂了:

const data5 = {
  a: 'abc',
  b: null,
  c: {
    x: 'xxx',
    y: 'yyy',
    z: 2046
  },
  d: 9527,
  toJSON: function() {
    return 'WTF'
  }
}

JSON.stringify(data5, null, 2);

// 结果返回  "WTF"

直接覆盖掉所有的序列化处理,返回了 "WTF"

下面的评论中 @yingyuk 给出了详细的 stringify转换demo

@ccforward ccforward self-assigned this Jul 26, 2017
@ccforward ccforward changed the title 64.深度使用 JSON.stringify() 64.深度使用 JSON.stringify() Jul 26, 2017
@jjeejj
Copy link

jjeejj commented Jul 27, 2017

推荐看我的这篇文章:JSON

@xwHoward
Copy link

xwHoward commented Jul 27, 2017

使用JSON.stringify()和JSON.parse()做对象的深拷贝:

var data = {
  a: 'abc',
  c: {
    x: {
      foo: 'xxx',
      bar: 'yyy'
    },
    y: 'yyy'
  }
};

var copy = JSON.stringify(data);
var copyObj = JSON.parse(copy);

@honpery
Copy link

honpery commented Jul 27, 2017

@xwHoward 所提到的深拷贝方法会忽略undefined和function,所以需要采用文章的方法再次封装 - -

@bouquetrender
Copy link

@xwHoward 这种深拷贝方式有什么副作用吗

@keizure
Copy link

keizure commented Jul 27, 2017

直接用扩展运算符

@lulusir
Copy link

lulusir commented Jul 27, 2017

@sakuyakun 副作用就是文中提到的undefined和function会忽略咯,还有就是不支持JSON的浏览器就用不了

@refanbanzhang
Copy link

比较有意思

@xxyj
Copy link

xxyj commented Jul 27, 2017

@lulusir 还有如果字符串中含有'\n',JSON.parse解析是会报错的

@xwHoward
Copy link

@honpery @sakuyakun @lulusir @xxyj 确实这种方法有一定局限性,但在对数据结构明了、性能要求比较高(相比用递归进行对象复制)的时候可以用这种方式快速进行对象深拷贝,算是一种trick啦

@yunchangyue
Copy link

很好

@qianlongo
Copy link

赞 可以的

@zhengsk
Copy link

zhengsk commented Jul 30, 2017

@xwHoward 按我的了解,该方法复制对象的性能比直接使用传统方式复制对象更慢。
https://jsperf.com/deep-copy-vs-json-stringify-json-parse/10

确实这种方法有一定局限性,但在对数据结构明了、性能要求比较高(相比用递归进行对象复制)的时候可以用这种方式快速进行对象深拷贝,算是一种trick啦

@chinghanho
Copy link

@lulusir 瀏覽器支援不是問題了。

screen shot 2017-07-31 at 10 16 22 am

@yingyuk
Copy link

yingyuk commented Aug 1, 2017

var myMap = new Map();
myMap.set(0, 'zero');

var mySet = new Set();
mySet.add(1);

class test {
  constructor(opt) {
    this.opt = opt;
  }
}

JSON.stringify({
  String: 'string',
  Boolean: true,
  Number: 123,
  NaN: NaN, // null,
  Infinity: Infinity, // null
  null: null, // null
  undefined: undefined, // 没有 key
  Array: [1, 2, 3],
  Object: {
    foo: 'bar',
  },
  Symbol: Symbol('foo'), // {}
  Map: myMap, // {}
  Set: mySet, // {}
  Promise: new Promise(function(resolve, reject) {}), // {}
  Proxy: new Proxy({}, {}), // {}
  Class: test, //  没有 key
  ClassA: new test(123), // { "opt": 123 },
  Math: Math, // {}
  // Buffer: new Buffer('123'), // nodeJs  '{"Buffer":{"type":"Buffer","data":[49,50,51]}}'
  Error: new Error('error'), // {}
  Function: function() { // 没有 key
    console.info('hi');
  },
  Date: new Date(), // "2017-08-01T13:52:48.628Z",
  RegExp: /RegExp/, // {}
}, null, 2);

/*
"{
  "String": "string",
  "Boolean": true,
  "Number": 123,
  "NaN": null,
  "Infinity": null,
  "null": null,
  "Array": [
    1,
    2,
    3
  ],
  "Object": {
    "foo": "bar"
  },
  "Map": {},
  "Set": {},
  "Promise": {},
  "Proxy": {},
  "ClassA": {
    "opt": 123
  },
  "Math": {},
  "Error": {},
  "Date": "2017-08-01T14:04:58.522Z",
  "RegExp": {}
}"
 */

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests