什么是最快的替代方法?
JSON.parse(JSON.stringify(x))
有一种更好/内置的方法可以对对象/数组执行深度克隆,但我还没有找到它。
有什么想法吗?
JSON.parse(JSON.stringify(x))
有一种更好/内置的方法可以对对象/数组执行深度克隆,但我还没有找到它。
有什么想法吗?
var deepClone = function (obj) {
try {
var names = Object.getOwnPropertyNames(obj);
} catch (e) {
if (e.message.indexOf("not an object") > -1) {
// is not object
return obj;
}
}
var proto = Object.getPrototypeOf(obj);
var clone = Object.create(proto);
names.forEach(function (name) {
var pd = Object.getOwnPropertyDescriptor(obj, name);
if (pd.value) {
pd.value = deepClone(pd.value);
}
Object.defineProperty(clone, name, pd);
});
return clone;
};
对于许多边缘情况,这种方法会失败。
如您所见,通常无法深度克隆对象而不破坏其特殊属性(例如数组中的.length
)。要修复此问题,您必须单独处理Array
,然后再单独处理每个特殊对象。
当您执行deepClone(document.getElementById("foobar"))
时,您期望会发生什么?
此外,浅拷贝很容易。
Object.getOwnPropertyDescriptors = function (obj) {
var ret = {};
Object.getOwnPropertyNames(obj).forEach(function (name) {
ret[name] = Object.getOwnPropertyDescriptor(obj, name);
});
return ret;
};
var shallowClone = function (obj) {
return Object.create(
Object.getPrototypeOf(obj),
Object.getOwnPropertyDescriptors(obj)
);
};
Array
及其 .length
魔法一样的特殊对象,我认为 Date
也有其中的魔法。还有宿主对象,是的。我不知道循环引用的简单解决方法是什么。 - RaynosDate
没有任何魔力。我们已经讨论过了 :-p - Andy Econsole.log((new String()).hasOwnProperty("length"), Object.create(String.prototype).hasOwnProperty("length"))
- “魔力”显然是在构造函数中完成的。 - Andy E这个问题的2022解决方案是使用structuredClone。
参考:https://developer.mozilla.org/en-US/docs/Web/API/structuredClone
structuredClone(x)
我实际上是在比较它和angular.copy
您可以在此处运行JSperf测试: https://jsperf.com/angular-copy-vs-json-parse-string
我在比较:
myCopy = angular.copy(MyObject);
vs
myCopy = JSON.parse(JSON.stringify(MyObject));
循环引用并不是真正的问题。我的意思是,它们确实是问题,但这只是一个适当记录的问题。无论如何,对于这个问题的快速答案,请查看:
https://github.com/greatfoundry/json-fu
在我疯狂的 JavaScript 实验室中,我一直在使用基本实现来序列化整个 Chromium 的 JavaScript 上下文,包括整个 DOM,并通过 WebSocket 将其发送到 Node 并成功地重新序列化。唯一有问题的循环问题是 navigator.mimeTypes 和 navigator.plugins 无限循环引用,但这很容易解决。(function(mimeTypes, plugins){
delete navigator.mimeTypes;
delete navigator.plugins;
var theENTIREwindowANDdom = jsonfu.serialize(window);
WebsocketForStealingEverything.send(theENTIREwindowANDdom);
navigator.mimeTypes = mimeTypes;
navigator.plugins = plugins;
})(navigator.mimeTypes, navigator.plugins);
// categorizeEverything takes any object and will sort its properties into high level categories
// based on it's profile in terms of what it can in JavaScript land. It accomplishes this task with a bafflingly
// small amount of actual code by being extraordinarily uncareful, forcing errors, and generally just
// throwing caution to the wind. But it does a really good job (in the one browser I made it for, Chrome,
// and mostly works in webkit, and could work in Firefox with a modicum of effort)
//
// This will work on any object but its primarily useful for sorting the shitstorm that
// is the webkit global context into something sane.
function categorizeEverything(container){
var types = {
// DOMPrototypes are functions that get angry when you dare call them because IDL is dumb.
// There's a few DOM protos that actually have useful constructors and there currently is no check.
// They all end up under Class which isn't a bad place for them depending on your goals.
// [Audio, Image, Option] are the only actual HTML DOM prototypes that sneak by.
DOMPrototypes: {},
// Plain object isn't callable, Object is its [[proto]]
PlainObjects: {},
// Classes have a constructor
Classes: {},
// Methods don't have a "prototype" property and their [[proto]] is named "Empty"
Methods: {},
// Natives also have "Empty" as their [[proto]]. This list has the big boys:
// the various Error constructors, Object, Array, Function, Date, Number, String, etc.
Natives: {},
// Primitives are instances of String, Number, and Boolean plus bonus friends null, undefined, NaN, Infinity
Primitives: {}
};
var str = ({}).toString;
function __class__(obj){ return str.call(obj).slice(8,-1); }
Object.getOwnPropertyNames(container).forEach(function(prop){
var XX = container[prop],
xClass = __class__(XX);
// dumping the various references to window up front and also undefineds for laziness
if(xClass == "Undefined" || xClass == "global") return;
// Easy way to rustle out primitives right off the bat,
// forcing errors for fun and profit.
try {
Object.keys(XX);
} catch(e) {
if(e.type == "obj_ctor_property_non_object")
return types.Primitives[prop] = XX;
}
// I'm making a LOT flagrant assumptions here but process of elimination is key.
var isCtor = "prototype" in XX;
var proto = Object.getPrototypeOf(XX);
// All Natives also fit the Class category, but they have a special place in our heart.
if(isCtor && proto.name == "Empty" ||
XX.name == "ArrayBuffer" ||
XX.name == "DataView" ||
"BYTES_PER_ELEMENT" in XX) {
return types.Natives[prop] = XX;
}
if(xClass == "Function"){
try {
// Calling every single function in the global context without a care in the world?
// There's no way this can end badly.
// TODO: do this nonsense in an iframe or something
XX();
} catch(e){
// Magical functions which you can never call. That's useful.
if(e.message == "Illegal constructor"){
return types.DOMPrototypes[prop] = XX;
}
}
// By process of elimination only regular functions can still be hanging out
if(!isCtor) {
return types.Methods[prop] = XX;
}
}
// Only left with full fledged objects now. Invokability (constructor) splits this group in half
return (isCtor ? types.Classes : types.PlainObjects)[prop] = XX;
// JSON, Math, document, and other stuff gets classified as plain objects
// but they all seem correct going by what their actual profiles and functionality
});
return types;
};
Node
有一个.cloneNode
方法? - RaynosJSON.parse(JSON.stringify({ a: '1', foo: (a) => a*a }))
。您将看到 foo 方法已被删除。 - Gil Epshtain