为什么JSON.stringify没有序列化原型值
Why is JSON.stringify not serializing prototype values?
我最近一直在Node.js和浏览器中进行一些JSON解析和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=={"initializedValue":"你可以看到我!"}
我希望:
a_string=={"initializedValue":"你能看到我!","uninitializedvalue":"看不到我!"}
更新(01-10-2019):
最后注意到@ncardeli的答案,它确实允许我们做以下事情来实现我的上述要求(在2019年!):
替换var a_string = JSON.stringify(a);
与var a_string = JSON.stringify(a, Object.keys(ClassA.prototype));
完整代码:
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, Object.keys(ClassA.prototype));
console.log(a_string)
因为这就是JSON的工作方式。来自ES5规范:
设K是一个内部字符串列表,由[[Enumerable]]属性为true的值的所有自身属性的名称组成。
这是有道理的,因为JSON规范中没有保存信息的机制,如果包含继承的属性,则需要将JSON字符串解析回JavaScript对象。在您的示例中,这将如何解析:
{"initializedValue":"你能看到我!","uninitializedValue:"你看不到我!}
除了具有2个键值对的平面对象之外,没有任何信息可以将其解析为其他内容。
仔细想想,JSON并不打算直接映射到JavaScript对象。其他语言必须能够将JSON字符串解析为名称-值对的简单结构。如果JSON字符串包含序列化完整JavaScript范围链所需的所有信息,那么其他语言可能无法将其解析为有用的信息。用Douglas Crockford在json.org上的话来说:
这些[哈希表和数组]是通用的数据结构。几乎所有现代编程语言都以这样或那样的形式支持它们。与编程语言可互换的数据格式也基于这些结构,这是有道理的。
我想补充一点,即使JSON.stringify
只会字符串化对象自己的属性,正如接受的答案中所解释的那样,您也可以通过指定String
的数组作为JSON.stringify
的第二个参数(称为替换数组)来更改字符串化过程的行为。
如果您指定一个String
数组,其中包含要字符串化的属性白名单,则字符串化算法将改变其行为,并将考虑原型链中的属性。
来自ES5规范:
如果PropertyList未定义,则
a。设K为PropertyList。
其他
a。设K是一个内部字符串列表,由其[[Enumerable]]属性为是的。字符串的顺序应与Object.keys标准内置函数。
如果你事先知道要字符串化的对象的属性的名称,你可以这样做:
var a_string = JSON.stringify(a, ["initialisedValue", "uninitialisedValue"]);
// a_string == { "initialisedValue" : "You can see me!", "uninitialisedValue" : "You can't see me!" }
还有另一种可能性,仍然可以将原型中的属性字符串化。
截至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"赋值时,您需要小心哪些属性是可枚举的或是隐式可枚举的。
另一种可能性是逐个手动分配您实际想要字符串化的每个属性。这可能不太容易出错。
- Ajax发布表单序列化,发布引号'
- 序列化数据属性中对象的最可靠方法
- YUI3 IO实用程序是否可以根据给定的内容类型标头值自动序列化数据
- 为什么JSON.stringify没有序列化原型值
- 有没有一个Nodejs库可以序列化和反序列化命名组件的路径(比如URL路径名)
- 是否可以在javascript中反序列化java对象
- jQuery Ajax数组序列化错误
- 对象序列化,JAVA,Javascript
- 在jquery中以序列化的形式传递额外的paparameter
- 如何使用angularjs序列化对象
- 使用System从C#集合创建JSON数组.网状物剧本序列化
- javascript中是否有更标准化的方法来转换(序列化)非表单数据以与ajax一起使用
- 在html标记中序列化javascript代码
- 如何在C#中反序列化json对象
- jQueryAJAX-将额外的键/值对推送到序列化的$_POST数组中
- 使用JSON序列化图论树的解决方法
- PHP未从AJAX接收序列化数据
- 如何序列化包括影子 DOM 在内的 HTML DOM
- 序列化表单时进行编码
- 原型仅序列化可见的表单字段值