Để thực hiện việc này, bạn sẽ muốn sử dụng chức năng "hồi sinh" khi phân tích cú pháp chuỗi JSON (và hàm "replacer" hoặc hàm toJSON
trên nguyên mẫu của hàm tạo của bạn khi tạo nó). Xem Section 15.12.2 và 15.12.3 đặc điểm kỹ thuật. Nếu môi trường của bạn chưa hỗ trợ phân tích cú pháp JSON gốc, bạn có thể sử dụng one of Crockford's parsers (Crockford là nhà phát minh JSON), cũng hỗ trợ chức năng "hồi sinh". Dưới đây là một ví dụ riêng biệt đơn giản hoạt động với các trình duyệt tuân theo ES5 (hoặc các thư viện mô phỏng hành vi ES5) (live copy, chạy trong Chrome hoặc Firefox hoặc tương tự), nhưng xem xét ví dụ về giải pháp tổng quát hơn.
// Our constructor function
function Foo(val) {
this.value = val;
}
Foo.prototype.nifty = "I'm the nifty inherited property.";
Foo.prototype.toJSON = function() {
return "/Foo(" + this.value + ")/";
};
// An object with a property, `foo`, referencing an instance
// created by that constructor function, and another `bar`
// which is just a string
var obj = {
foo: new Foo(42),
bar: "I'm bar"
};
// Use it
display("obj.foo.value = " + obj.foo.value);
display("obj.foo.nifty = " + obj.foo.nifty);
display("obj.bar = " + obj.bar);
// Stringify it with a replacer:
var str = JSON.stringify(obj);
// Show that
display("The string: " + str);
// Re-create it with use of a "reviver" function
var obj2 = JSON.parse(str, function(key, value) {
if (typeof value === "string" &&
value.substring(0, 5) === "/Foo(" &&
value.substr(-2) == ")/"
) {
return new Foo(value.substring(5, value.length - 2));
}
return value;
});
// Use the result
display("obj2.foo.value = " + obj2.foo.value);
display("obj2.foo.nifty = " + obj2.foo.nifty);
display("obj2.bar = " + obj2.bar);
Lưu ý toJSON
trên Foo.prototype
, và các chức năng chúng tôi vượt qua thành JSON.parse
.
Vấn đề ở đó, mặc dù, là trình thu thập được kết hợp chặt chẽ với nhà xây dựng Foo
. Thay vào đó, bạn có thể áp dụng một khuôn khổ chung trong mã của bạn, ở đó bất kỳ hàm xây dựng nào cũng có thể hỗ trợ hàm fromJSON
(hoặc tương tự) và bạn có thể chỉ sử dụng một trình hồi sinh tổng quát.
Dưới đây là một ví dụ về một người bình phục khái quát hóa mà tìm kiếm một tài sản ctor
và một tài sản data
, và kêu gọi ctor.fromJSON
nếu tìm thấy, đi qua trong toàn bộ giá trị nó nhận được (live example):
// A generic "smart reviver" function.
// Looks for object values with a `ctor` property and
// a `data` property. If it finds them, and finds a matching
// constructor that has a `fromJSON` property on it, it hands
// off to that `fromJSON` fuunction, passing in the value.
function Reviver(key, value) {
var ctor;
if (typeof value === "object" &&
typeof value.ctor === "string" &&
typeof value.data !== "undefined") {
ctor = Reviver.constructors[value.ctor] || window[value.ctor];
if (typeof ctor === "function" &&
typeof ctor.fromJSON === "function") {
return ctor.fromJSON(value);
}
}
return value;
}
Reviver.constructors = {}; // A list of constructors the smart reviver should know about
Để tránh phải lặp lại logic thông thường trong toJSON
và fromJSON
chức năng, bạn có thể có phiên bản generic:
// A generic "toJSON" function that creates the data expected
// by Reviver.
// `ctorName` The name of the constructor to use to revive it
// `obj` The object being serialized
// `keys` (Optional) Array of the properties to serialize,
// if not given then all of the objects "own" properties
// that don't have function values will be serialized.
// (Note: If you list a property in `keys`, it will be serialized
// regardless of whether it's an "own" property.)
// Returns: The structure (which will then be turned into a string
// as part of the JSON.stringify algorithm)
function Generic_toJSON(ctorName, obj, keys) {
var data, index, key;
if (!keys) {
keys = Object.keys(obj); // Only "own" properties are included
}
data = {};
for (index = 0; index < keys.length; ++index) {
key = keys[index];
data[key] = obj[key];
}
return {ctor: ctorName, data: data};
}
// A generic "fromJSON" function for use with Reviver: Just calls the
// constructor function with no arguments, then applies all of the
// key/value pairs from the raw data to the instance. Only useful for
// constructors that can be reasonably called without arguments!
// `ctor` The constructor to call
// `data` The data to apply
// Returns: The object
function Generic_fromJSON(ctor, data) {
var obj, name;
obj = new ctor();
for (name in data) {
obj[name] = data[name];
}
return obj;
}
Ưu điểm ở đây là bạn de fer để thực hiện một "loại" cụ thể (cho thiếu một thuật ngữ tốt hơn) cho nó serializes và deserializes. Vì vậy, bạn có thể có một "loại" mà chỉ sử dụng Generics:
// `Foo` is a constructor function that integrates with Reviver
// but doesn't need anything but the generic handling.
function Foo() {
}
Foo.prototype.nifty = "I'm the nifty inherited property.";
Foo.prototype.spiffy = "I'm the spiffy inherited property.";
Foo.prototype.toJSON = function() {
return Generic_toJSON("Foo", this);
};
Foo.fromJSON = function(value) {
return Generic_fromJSON(Foo, value.data);
};
Reviver.constructors.Foo = Foo;
... hoặc một trong đó, vì lý do gì, đã làm một cái gì đó nhiều tùy chỉnh:
// `Bar` is a constructor function that integrates with Reviver
// but has its own custom JSON handling for whatever reason.
function Bar(value, count) {
this.value = value;
this.count = count;
}
Bar.prototype.nifty = "I'm the nifty inherited property.";
Bar.prototype.spiffy = "I'm the spiffy inherited property.";
Bar.prototype.toJSON = function() {
// Bar's custom handling *only* serializes the `value` property
// and the `spiffy` or `nifty` props if necessary.
var rv = {
ctor: "Bar",
data: {
value: this.value,
count: this.count
}
};
if (this.hasOwnProperty("nifty")) {
rv.data.nifty = this.nifty;
}
if (this.hasOwnProperty("spiffy")) {
rv.data.spiffy = this.spiffy;
}
return rv;
};
Bar.fromJSON = function(value) {
// Again custom handling, for whatever reason Bar doesn't
// want to serialize/deserialize properties it doesn't know
// about.
var d = value.data;
b = new Bar(d.value, d.count);
if (d.spiffy) {
b.spiffy = d.spiffy;
}
if (d.nifty) {
b.nifty = d.nifty;
}
return b;
};
Reviver.constructors.Bar = Bar;
Và dưới đây là cách chúng tôi có thể kiểm tra rằng Foo
và Bar
công việc như mong đợi (live copy):
// An object with `foo` and `bar` properties:
var before = {
foo: new Foo(),
bar: new Bar("testing", 42)
};
before.foo.custom = "I'm a custom property";
before.foo.nifty = "Updated nifty";
before.bar.custom = "I'm a custom property"; // Won't get serialized!
before.bar.spiffy = "Updated spiffy";
// Use it
display("before.foo.nifty = " + before.foo.nifty);
display("before.foo.spiffy = " + before.foo.spiffy);
display("before.foo.custom = " + before.foo.custom + " (" + typeof before.foo.custom + ")");
display("before.bar.value = " + before.bar.value + " (" + typeof before.bar.value + ")");
display("before.bar.count = " + before.bar.count + " (" + typeof before.bar.count + ")");
display("before.bar.nifty = " + before.bar.nifty);
display("before.bar.spiffy = " + before.bar.spiffy);
display("before.bar.custom = " + before.bar.custom + " (" + typeof before.bar.custom + ")");
// Stringify it with a replacer:
var str = JSON.stringify(before);
// Show that
display("The string: " + str);
// Re-create it with use of a "reviver" function
var after = JSON.parse(str, Reviver);
// Use the result
display("after.foo.nifty = " + after.foo.nifty);
display("after.foo.spiffy = " + after.foo.spiffy);
display("after.foo.custom = " + after.foo.custom + " (" + typeof after.foo.custom + ")");
display("after.bar.value = " + after.bar.value + " (" + typeof after.bar.value + ")");
display("after.bar.count = " + after.bar.count + " (" + typeof after.bar.count + ")");
display("after.bar.nifty = " + after.bar.nifty);
display("after.bar.spiffy = " + after.bar.spiffy);
display("after.bar.custom = " + after.bar.custom + " (" + typeof after.bar.custom + ")");
display("(Note that after.bar.custom is undefined because <code>Bar</code> specifically leaves it out.)");
Tôi hoan nghênh bạn @Cystack! Khái niệm chuyển các đối tượng Javascript (đó là các thuộc tính dữ liệu thuần túy, không có phương thức nào) được thảo luận ở đây rất nhiều. Tuy nhiên, đây là câu hỏi đầu tiên mà tôi gặp phải thực sự tập trung vào các đối tượng, đó là các đối tượng với các phương thức. Phương thức 'JSON.stringify' bình thường mang đủ cạm bẫy (tham chiếu tuần hoàn, thoát/bộ ký tự, v.v.), do đó hầu hết câu hỏi tập trung vào điều đó và rất đáng buồn tôi có thể tìm thấy về" tự động khôi phục "này. – humanityANDpeace