为什么JSON.stringify不序列化不可枚举属性?

k0pti3hp  于 2023-02-10  发布在  其他
关注(0)|答案(3)|浏览(172)

我用JavaScript将对象序列化为JSON字符串,
我注意到只有可枚举的对象属性才被序列化:

var a = Object.create(null,{
  x: { writable:true, configurable:true, value: "hello",enumerable:false },
  y: { writable:true, configurable:true, value: "hello",enumerable:true }
});
document.write(JSON.stringify(a)); //result is {"y":"hello"}

[一个月一次]
我想知道这是为什么,我已经搜索了MDN pagejson2解析器文档,但是在任何地方都找不到这种行为的文档。
我怀疑这是使用for... in循环的结果,for... in循环只通过enumerable属性(至少在json2的情况下)。这可能可以通过像Object.getOwnPropertyNames这样既返回可枚举属性又返回不可枚举属性的东西来完成。尽管这可能会导致序列化问题(由于反序列化)。

    • TL;医生**
  • 为什么JSON.stringify只序列化可枚举属性?
  • 这种行为是否有记录?
  • 我如何自己实现非枚举属性的序列化?
p8h8hvxi

p8h8hvxi1#

ES5规范中有规定。
如果Type(值)为Object,且IsCallable(值)为false

If the [[Class]] internal property of value is "Array" then

    Return the result of calling the abstract operation JA with argument value.

Else, return the result of calling the abstract operation JO with argument value.

那么,我们来看一下JO,相关部分如下:
设K是一个内部字符串列表,由**所有其Enumerable属性为真的值的自身属性的名称组成。字符串的排序应该与Object.keys标准内置函数所使用的排序相同。

svujldwt

svujldwt2#

就像@ThiefMaster上面回答的那样,它在规范中指定了
但是,如果您知道您希望提前序列化的不可枚举属性的名称,则可以通过将replacer函数作为第二个参数传递给JSON.stringify()(MDN文档)来实现,如下所示

var o = {
  prop: 'propval',
}

Object.defineProperty(o, 'propHidden', {
  value: 'propHiddenVal',
  enumerable: false,
  writable: true,
  configurable: true
});

var s = JSON.stringify(o, (key, val) => {
  if (!key) {
    // Initially, the replacer function is called with an empty string as key representing the object being stringified. It is then called for each property on the object or array being stringified.
    if (typeof val === 'object' && val.hasOwnProperty('propHidden')) {
      Object.defineProperty(val, 'propHidden', {
        value: val.propHidden,
        enumerable: true,
        writable: true,
        configurable: true
      });

    }
  }
  return val;
});

console.log(s);
qij5mzcb

qij5mzcb3#

你可以用下面的代码***实现一个包含非枚举项的泛型JSON.stringify(),如***所示。

  • 此代码经过彻底测试,以确定是否存在可能的错误或意外行为;同样,它把函数变成了基本对象。2相应地对待。
  • copyEnumerable()是一个值得注意的函数,它本身并没有在内部调用JSON.stringify()(你可以自己用output对象来调用),主要是因为递归调用JSON.stringify()(由于嵌套对象)会产生更难看的、不想要的结果。
  • 我只检查要特殊处理的objectfunction类型;我相信这些是唯一需要特殊处理的类型......注意JS Arrays[])属于这一类别(对象),以及类(函数),而null不属于这一类别。由于nullobject类型,所以我包含了!!检查。
  • stopRecursiveCopy``Set只是为了确保它没有做无限递归。
  • 我使用了一个技巧,在JSON.stringify()调用中添加了两个额外的参数,使其输出的内容格式更美观,便于阅读。

代码采用了一种易于在控制台中试用和调整的格式:

{
  o = {};
  d = {};
  Object.defineProperties(o, {
    a: {
      value: 5,
      enumerable: false
    },
    b: {
      value: "test",
      enumerable: false
    },
    c: {
      value: {},
      enumerable: false
    }
  });
  Object.defineProperty(o.c, "d", {
    value: 7,
    enumerable: false
  });

  //The code to use (after careful consideration!).
  function isObject(testMe) {
    return ((typeof(testMe) === "object" && !!testMe) ||
      typeof(testMe) === "function");
  }
  let stopRecursiveCopy = new Set();
  function copyEnumerable(obj) {
    if (!isObject(obj)) {
      return obj;
    }
    let enumerableCopy = {};
    for (let key of Object.getOwnPropertyNames(obj)) {
      if (isObject(obj[key])) {
        if (!stopRecursiveCopy.has(obj[key])) {
          stopRecursiveCopy.add(obj[key]);
          enumerableCopy[key] = copyEnumerable(obj[key]);
          stopRecursiveCopy.delete(obj[key]);
        }
      } else {
        enumerableCopy[key] = obj[key];
      }
    }
    return enumerableCopy;
  }

  console.log(JSON.stringify(copyEnumerable(o), null, "  "));

  Object.defineProperty(copyEnumerable, "test", {
    value: 10,
    enumerable: false
  });
  console.log(JSON.stringify(copyEnumerable(copyEnumerable), null, "  "));

  Object.defineProperty(o, "f", {
    value: copyEnumerable,
    enumerable: false
  });
  console.log(JSON.stringify(copyEnumerable(o), null, "  "));
}

其输出:

//o
{
  "a": 5,
  "b": "test",
  "c": {
    "d": 7
  }
}

//copyEnumerable itself
{
  "test": 10,
  "prototype": {
    "constructor": {
      "test": 10,
      "length": 1,
      "name": "copyEnumerable"
    }
  },
  "length": 1,
  "name": "copyEnumerable"
}

//o again, but with `f: copyEnumerable` added
{
  "a": 5,
  "b": "test",
  "c": {
    "d": 7
  },
  "f": {
    "test": 10,
    "prototype": {},
    "length": 1,
    "name": "copyEnumerable"
  }
}

相关链接:

相关问题