我最近一直在使用JSON解析并在Node.js和浏览器中传递Javascript,并遇到了这个难题。
我使用构造函数创建的任何对象都无法通过JSON.stringify完全序列化,除非我单独初始化构造函数中的所有值!这意味着我的原型在设计这些类时基本没用。
有人可以解释为什么以下不会像我预期的那样序列化吗?
var ClassA = function () { this.initialisedValue = "You can see me!" };
ClassA.prototype = { initialisedValue : "You can't see me!", uninitialisedValue : "You can't see me!" };
var a = new ClassA();
var a_string = JSON.stringify(a);
会发生什么:
a_string == {“initialisedValue”:“你可以看到我!” }
我希望:
a_string == {“initialisedValue”:“你可以看到我!”,“uninitialisedValue”:“你看不到我!” }
答案 0 :(得分:13)
只是因为这是JSON的工作方式。来自ES5 spec:
设K是一个内部字符串列表,由[[Enumerable]]属性为true的值的所有自有属性的名称组成。
这是有道理的,因为JSON规范中没有用于保留在包含继承属性时将JSON字符串解析回JavaScript对象所需的信息的机制。在您的示例中,将如何解析:
{“initialisedValue”:“你可以看到我!”,“uninitialisedValue”:“你看不到我!” }
没有信息可以将其解析为具有2个键值对的扁平对象以外的任何其他内容。
如果你考虑一下,JSON并不打算直接映射到JavaScript对象。其他语言必须能够将JSON字符串解析为名称 - 值对的简单结构。如果JSON字符串包含序列化完整JavaScript范围链所需的所有信息,则其他语言可能无法将其解析为有用的东西。用Douglas Crockford的话说json.org:
这些[哈希表和数组]是通用数据结构。实际上,所有现代编程语言都以某种形式支持它们。有意义的是,可与编程语言互换的数据格式也基于这些结构。
答案 1 :(得分:0)
我在使用mongoose时登陆此页面:如果其他用户遇到类似问题,则以下问题(以及相关答案)可能会有用:
How do you turn a Mongoose document into a plain object?
(基本上:使用Model' toObject()方法)
答案 2 :(得分:0)
我想补充一点,即使JSON.stringify
只会对对象自己的属性进行字符串化,如接受的答案中所述,您可以通过指定{{1}数组来改变字符串化过程的行为。作为String
的第二个参数(称为替换器数组)。
如果指定一个JSON.stringify
数组,其中包含属性的白名单以进行字符串化,则字符串化算法将更改其行为,并将考虑原型链中的属性。
来自ES5 spec:
如果PropertyList未定义,则
一个。设K为PropertyList。
- 醇>
否则
一个。设K是一个由字符串组成的内部字符串列表 [[Enumerable]]属性为的所有值的属性 真正。字符串的顺序应与使用的顺序相同 Object.keys标准内置函数。
如果你事先知道要字符串化的对象属性的名称,你可以这样做:
String
答案 3 :(得分:0)
还有一种可能性可以保留原型中待定义的属性。
根据JSON规范(15.12.3字符串化http://es5.github.io/#x15.12.3):
[...]
2. If Type(value) is Object, then
a. Let toJSON be the result of calling the [[Get]] internal method of value with argument "toJSON".
b. If IsCallable(toJSON) is true
i. Let value be the result of calling the [[Call]] internal method of toJSON passing value as the this value and with an argument list consisting of key.
[...]
因此yu可以编写自己的JSON字符串化函数。通用的实现方式可能是:
class Cat {
constructor(age) {
this.age = age;
}
get callSound() {
// This does not work as this getter-property is not enumerable
return "Meow";
}
toJSON() {
const jsonObj = {}
const self = this; // If you can use arrow functions just use 'this'-keyword.
// Object.keys will list all 'enumerable' properties
// First we look at all own properties of 'this'
Object.keys(this).forEach(function(k) {
jsonObj[k] = self[k];
});
// Then we look at all own properties of this's 'prototype'
Object.keys(Object.getPrototypeOf(this)).forEach(function(k) {
jsonObj[k] = self[k];
});
return JSON.stringify(jsonObj);
}
}
Object.defineProperty(Cat.prototype, 'callSound2', {
// The 'enumerable: true' is important!
value: "MeowMeow", enumerable: true
});
let aCat = new Cat(4);
console.log(JSON.stringify(aCat));
// prints "{\"age\":4,\"callSound2\":\"MeowMeow\"}"
只要正确的属性(您实际上想对JSON进行字符串化)是可枚举的,而您不希望对它们进行字符串化的属性,则可以使用。因此,当您将值分配给“ this”时,您需要注意使哪些属性可枚举或隐式枚举。
另一种可能性是,逐个手动分配您实际想要被字符串化的每个属性。可能不太容易出错。