Подтвердить что ты не робот

Клонирование: какая самая быстрая альтернатива JSON.parse(JSON.stringify(x))?

Какая самая быстрая альтернатива

JSON.parse(JSON.stringify(x))

Должен быть более удобный/встроенный способ выполнения глубокого клонирования объектов/массивов, но я еще не нашел его.

Любые идеи?

4b9b3361

Ответ 1

Нет, нет возможности построить глубокие объекты клонирования.

И глубокое клонирование - сложная и кропотливая вещь, с которой нужно иметь дело.

Предположим, что метод 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)
    );
};

Ответ 2

Я фактически сравнивал его с angular.copy

Здесь вы можете запустить тест JSperf: https://jsperf.com/angular-copy-vs-json-parse-string

Я сравниваю:

myCopy = angular.copy(MyObject);

против

myCopy = JSON.parse(JSON.stringify(MyObject));

Это самый полный из всех тестов, которые я мог бы запускать на всех моих компьютерах введите описание изображения здесь

Ответ 3

Циклические ссылки на самом деле не являются проблемой. Я имею в виду, что это всего лишь вопрос правильного ведения записей. Во всяком случае, быстрый ответ для этого. Проверьте это:

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;
};