将 JSON 字符串解析为 JavaScript 中的特定对象原型

IT技术 javascript json parsing object prototype
2021-01-25 09:30:30

我知道如何解析 JSON 字符串并将其转换为 JavaScript 对象。您可以JSON.parse()在现代浏览器(和 IE9+)中使用。

太好了,但是我怎样才能将该 JavaScript 对象转换为特定的JavaScript 对象(即具有特定原型)?

例如,假设您有:

function Foo()
{
   this.a = 3;
   this.b = 2;
   this.test = function() {return this.a*this.b;};
}
var fooObj = new Foo();
alert(fooObj.test() ); //Prints 6
var fooJSON = JSON.parse({"a":4, "b": 3});
//Something to convert fooJSON into a Foo Object
//....... (this is what I am missing)
alert(fooJSON.test() ); //Prints 12

同样,我并不想知道如何将 JSON 字符串转换为通用 JavaScript 对象。我想知道如何将 JSON 字符串转换为“Foo”对象。也就是说,我的对象现在应该有一个函数“test”和属性“a”和“b”。

更新 在做了一些研究之后,我想到了这个......

Object.cast = function cast(rawObj, constructor)
{
    var obj = new constructor();
    for(var i in rawObj)
        obj[i] = rawObj[i];
    return obj;
}
var fooJSON = Object.cast({"a":4, "b": 3}, Foo);

那行得通吗?

2017 年 5 月更新:执行此操作的“现代”方式是通过Object.assign,但此功能在 IE 11 或更旧的 Android 浏览器中不可用。

6个回答

当前的答案包含大量手卷或库代码。这是没有必要的。

  1. 使用JSON.parse('{"a":1}')创建一个普通的对象。

  2. 使用标准化函数之一来设置原型:

    • Object.assign(new Foo, { a: 1 })
    • Object.setPrototypeOf({ a: 1 }, Foo.prototype)
查看下面我的解决方案,该解决方案以递归方式应用 Object.assign(..) 可以自动解析属性(预先提供一些信息)
2021-03-13 09:30:30
如果某些属性也需要原型,则设置原型的解决方案不起作用。换句话说:它只解决第一级数据层次结构。
2021-03-17 09:30:30
@SimonEpskamp 该代码不起作用。检查您的 url,第二个参数setPrototypeOf是属性描述符。
2021-04-07 09:30:30
Object.assign 在旧版浏览器中不可用,包括 IE 和旧版 Android 浏览器。kangax.github.io/compat-table/es6/...
2021-04-11 09:30:30
还有一个很大的警告不要使用Object.setPrototypeOf(...). developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/...
2021-04-11 09:30:30

请参阅下面的示例(此示例使用本机 JSON 对象)。我的更改在大写中注释:

function Foo(obj) // CONSTRUCTOR CAN BE OVERLOADED WITH AN OBJECT
{
    this.a = 3;
    this.b = 2;
    this.test = function() {return this.a*this.b;};

    // IF AN OBJECT WAS PASSED THEN INITIALISE PROPERTIES FROM THAT OBJECT
    for (var prop in obj) this[prop] = obj[prop];
}

var fooObj = new Foo();
alert(fooObj.test() ); //Prints 6

// INITIALISE A NEW FOO AND PASS THE PARSED JSON OBJECT TO IT
var fooJSON = new Foo(JSON.parse('{"a":4,"b":3}'));

alert(fooJSON.test() ); //Prints 12
GagleKas 的建议值得一听。(虽然“非常危险”有点OTT。)上面的例子只是给你一个想法。正确的实现将取决于您的应用程序。
2021-03-20 09:30:30
我想你也可以做这个的“相反”。构造一个空白的 Foo 对象并将属性从 fooJSON 复制到新的 Foo 对象中。最后,将 fooJSON 设置为指向 Foo 对象。
2021-03-21 09:30:30
这是非常危险的。如果 obj 有一个不在 Foo 定义中的属性,您将创建一个带有额外隐藏属性的 Foo 对象,您不知道它的名称......我会简单地做而不是循环:this.a = obj。 a 和 this.b = obj.b。或者直接将“a”和“b”作为参数传递:new Foo (obj.a, obj.b)
2021-03-24 09:30:30
您可能希望保护自己免受原型属性的影响。 for (var prop in obj) {if (obj.hasOwnProperty(prop)) {this[prop] = obj[prop];}}
2021-04-01 09:30:30
@RomainVergnory 为了更加安全,我只初始化在构造函数中创建的属性,而不是 obj: for (var prop in obj) {if (this.hasOwnProperty(prop)) {this[prop] = obj[prop];}}这假设您希望服务器填充所有属性,如果 obj.hasOwnProperty() 失败,IMO 也应该抛出...
2021-04-09 09:30:30

您要添加 JSON 序列化/反序列化功能,对吗?然后看看这个:

你想实现这个:

统一语言学

toJson() 是一个普通的方法。
fromJson() 是一个静态方法。

实施

var Book = function (title, author, isbn, price, stock){
    this.title = title;
    this.author = author;
    this.isbn = isbn;
    this.price = price;
    this.stock = stock;

    this.toJson = function (){
        return ("{" +
            "\"title\":\"" + this.title + "\"," +
            "\"author\":\"" + this.author + "\"," +
            "\"isbn\":\"" + this.isbn + "\"," +
            "\"price\":" + this.price + "," +
            "\"stock\":" + this.stock +
        "}");
    };
};

Book.fromJson = function (json){
    var obj = JSON.parse (json);
    return new Book (obj.title, obj.author, obj.isbn, obj.price, obj.stock);
};

用法

var book = new Book ("t", "a", "i", 10, 10);
var json = book.toJson ();
alert (json); //prints: {"title":"t","author":"a","isbn":"i","price":10,"stock":10}

var book = Book.fromJson (json);
alert (book.title); //prints: t

注意:如果您愿意,您可以通过、 等更改所有属性定义,例如this.titlethis.authorvar titlevar author并向它们添加 getter 以完成 UML 定义。

是的,我知道,我的回答是一个例子,也是该问题的最佳答案,但是......甚至不是一个积极的观点......我不知道为什么我要浪费时间帮助别人
2021-03-24 09:30:30
这些天我会使用JSON.stringify()而不是自己编写 toJSon() 。现在所有现代浏览器都支持它,因此无需重新发明轮子。
2021-03-26 09:30:30
同意@skypecakes。如果您只想序列化属性的子集,请创建一个可序列化属性的常量。serializable = ['title', 'author', ...].JSON.stringify(serializable.reduce((obj, prop) => {...obj, [prop]: this[prop]}, {}))
2021-04-02 09:30:30
不要忘记您的toJson()方法 - 无论它是否具有硬编码的单个属性或为每个属性使用 a - 都需要为每个字符串属性中可能存在的某些字符添加反斜杠转义码。(例如,书名可能有引号。)
2021-04-03 09:30:30
我同意。这个实现肯定会起作用,而且它很棒……只是有点冗长并且特定于 Book 对象。恕我直言,JS 的力量来自原型以及如果您愿意,可以拥有一些额外的属性。这就是我要说的。我真的在寻找单线: x.__proto__ = X.prototype; (虽然目前不兼容IE浏览器)
2021-04-11 09:30:30

我发现有用的一篇博文: Understanding JavaScript Prototypes

你可以弄乱对象的 __proto__ 属性。

var fooJSON = jQuery.parseJSON({"a":4, "b": 3});
fooJSON.__proto__ = Foo.prototype;

这允许 fooJSON 继承 Foo 原型。

我不认为这在 IE 中有效,但是......至少从我读过的内容来看。

我已经对更改的性能进行了一些测试,[[prototype]]它在 Chrome 中似乎无关紧要。在 Firefox 中,调用new比使用原型慢,而 Object.create 最快。我猜 FF 的问题是第一次测试比最后一次测试慢,只是执行顺序很重要。在 chrome 中,一切都以几乎相同的速度运行。我的意思是属性访问和方法调用。新的肌酸更快,但这并不重要。请参阅: jsperf.com/prototype-change-test-8874874/1和:jsperf.com/prototype-changed-method-call
2021-03-26 09:30:30
请注意,__proto__早就被弃用了此外,出于性能原因,不建议修改已创建对象的 [[Prototype]] 内部属性(通过设置__proto__或任何其他方式)。
2021-03-27 09:30:30
唉,没有一个实际未弃用的解决方案比这更复杂……
2021-03-28 09:30:30
我想这些天,人们会打电话Object.setPrototypeOf(fooJSON, Foo.prototype)而不是设置fooJSON.__proto__......对吗?
2021-03-28 09:30:30
事实上,这样的事情是我的第一直觉。
2021-04-04 09:30:30

我是否遗漏了问题中的某些内容,或者为什么没有人提到自 2011 年以来的reviver参数JSON.parse

这是有效的解决方案的简单代码:https : //jsfiddle.net/Ldr2utrr/

function Foo()
{
   this.a = 3;
   this.b = 2;
   this.test = function() {return this.a*this.b;};
}


var fooObj = new Foo();
alert(fooObj.test() ); //Prints 6
var fooJSON = JSON.parse(`{"a":4, "b": 3}`, function(key,value){
if(key!=="") return value; //logic of course should be more complex for handling nested objects etc.
  let res = new Foo();
  res.a = value.a;
  res.b = value.b;
  return res;
});
// Here you already get Foo object back
alert(fooJSON.test() ); //Prints 12

PS:您的问题令人困惑:>>太好了,但是我怎样才能将该 JavaScript 对象转换为特定的 JavaScript 对象(即具有特定原型)? 与标题相矛盾,在那里您询问 JSON 解析,但引用的段落询问 JS 运行时对象原型替换。