Какая самая быстрая альтернатива
JSON.parse(JSON.stringify(x))
Должен быть более удобный/встроенный способ выполнения глубокого клонирования объектов/массивов, но я еще не нашел его.
Любые идеи?
Какая самая быстрая альтернатива
JSON.parse(JSON.stringify(x))
Должен быть более удобный/встроенный способ выполнения глубокого клонирования объектов/массивов, но я еще не нашел его.
Любые идеи?
Нет, нет возможности построить глубокие объекты клонирования.
И глубокое клонирование - сложная и кропотливая вещь, с которой нужно иметь дело.
Предположим, что метод deepClone(a)
должен вернуть "глубокий клон" b.
Теперь "глубокий клон" - это объект с тем же [[Prototype]] и имеющий все собственные свойства, клонированные.
Для каждого клонированного свойства, которое клонируется, если у него есть собственные свойства, которые могут быть клонированы, тогда сделайте это, рекурсивно.
Конечно, поддерживали метаданные, связанные с такими свойствами, как [[Writable]] и [[Enumerable]] in-tact. И мы просто вернем вещь, если это не объект.
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)
);
};
Я фактически сравнивал его с angular.copy
Здесь вы можете запустить тест JSperf: https://jsperf.com/angular-copy-vs-json-parse-string
Я сравниваю:
myCopy = angular.copy(MyObject);
против
myCopy = JSON.parse(JSON.stringify(MyObject));
Это самый полный из всех тестов, которые я мог бы запускать на всех моих компьютерах
Циклические ссылки на самом деле не являются проблемой. Я имею в виду, что это всего лишь вопрос правильного ведения записей. Во всяком случае, быстрый ответ для этого. Проверьте это:
https://github.com/greatfoundry/json-fu
В моей сумасшедшей научной лаборатории сумасшедшего хакерства javascript я использовал базовую реализацию для сериализации целого javascript-контекста, включая всю DOM из Chromium, отправляя ее через websocket на Node и успешно ресериализуя ее, Единственным циклическим вопросом, который является проблематичным, является retardo 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);
JSONFu использует тактику создания Sigils, которые представляют более сложные типы данных. Как и MoreSigil, которые говорят, что элемент сокращен, и есть X уровней глубже, которые можно запросить. Важно понимать, что если вы сериализуете ВСЕ, то, очевидно, сложнее вернуть его обратно в исходное состояние. Я экспериментировал с различными вещами, чтобы увидеть, что возможно, что разумно и в конечном итоге, что идеально. Для меня цель немного более благоприятна, чем большинство потребностей в том, что я пытаюсь приблизиться к объединению двух разрозненных и одновременных контекстов javascript в разумное приближение одного контекста. Или определить, какой лучший компромисс заключается в раскрытии желаемых возможностей, не вызывая проблем с производительностью. Когда вы начинаете искать регенераторы для функций, вы пересекаете землю из сериализации данных в вызове удаленной процедуры.
Чистая хакерская функция, которую я приготовил по пути, классифицирует все свойства объекта, который вы передаете ему, в определенные категории. Цель его создания заключалась в том, чтобы иметь возможность пропускать оконный объект в Chrome и выплевывать свойства, организованные тем, что требуется для сериализации, а затем оживить их в удаленном контексте. Кроме того, чтобы выполнить это без каких-либо предустановленных списков чит-листа, как совершенно тупой контролер, который делает определения, подталкивая переданное значение палкой. Это было спроектировано и когда-либо проверено в Chrome и очень не является производственным кодом, но это классный образец.
// categorizeEverything takes any object and will sort its properties into high level categories
// based on it 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 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 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 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;
};