Как стать автором
Обновить

DeepClone на javascript, который можно твитнуть

Время на прочтение3 мин
Количество просмотров7.3K
Добрый день!

Не знаю, будет ли кому интересно, но соорудил сегодня такое чудо: DeepClone, упихивающийся в 140 байт.
Если кто-то такое уже делал и постил, ткните, пожалуйста, носом. Я в формате твитов нашёл только неглубокие копирования. Ну и не исключаю, что какой-нибудь применённый хак находится в списке запрещённых препаратов приёмов, а за его использование полагаются страшные кары :)

Из недостатков — всё то же, что и у клонирования с помощью функции extend из jQuery: не ест стандартные объекты типов Boolean, String, Date, игнорирует prototype и constructor и зависает на кольцах.
Достоинство одно и, по-большей части, чисто эстетическое — размер.

Тестировал в Firefox, Chromium, Opera, IE8 и в умолчальном андроидном браузере.

Под катом — код и небольшой рассказ о том, как это работает.



Для начала, читабельная версия (назвается dup потому, что иначе в 140 байт не сжимается):
function dup(o) {
    // "string", number, boolean
    if(typeof(o) != "object") {
        return o;
    }
    
     // null
    if(!o) {
        return o; // null
    }
    
    var r = (o instanceof Array) ? [] : {};
    for(var i in o) {
        if(o.hasOwnProperty(i)) {
            r[i] = dup(o[i]);
        }
    }
    return r;
}


Что здесь происходит:

Если объект — простой (строка, число, boolean), просто возвращаем его, дальше проверяем, что объект — не null (typeof(null) тоже == «object»). Теперь создаём результат (массив или объект) и пробегаем по свойствам, рекурсивно их клонируя.

В общем, всё просто. Теперь почти 300 байт надо ужать в два раза.

Вспоминаем некоторые вещи, которые нам помогут:
— typeof — это оператор, и ему скобки не нужны;
— у ?: приоритет самый низкий, так что скобки слева опять-таки можно опустить;
— null && {} — это null, а obj && {} — это obj;
— for(var i in null) проходит без ошибки, не делая ни одной итерации;
— параметры функции — тоже переменные, а вот передавать их все совершенно не обязательно. Это поможет нам сэкономить 4 байта на слове var с пробелом.

Исходя из этого, получаем:
function dup(o,i,r) {
    if(typeof o != "object") return o;
    r = o instanceof Array ? [] : o&&{};
    for(i in o)
        if(o.hasOwnProperty(i))
            r[i] = dup(o[i]);
    return r
}


Ну, или в одну строку (139 букв):
function dup(o,i,r){if(typeof o!="object")return o;r=o instanceof Array?[]:o&&{};for(i in o)if(o.hasOwnProperty(i))r[i]=dup(o[i]);return r}

Если же увеличить допустимый размер до 150-и символов, то можно добавить ещё и обработку ссылок на самих себя (не полное разруливание колец, конечно, но хоть что-то):
r[i] = (o[i] === o) ? r : dup(o[i]);

Или:
function dup(o,i,r){if(typeof o!="object")return o;r=o instanceof Array?[]:o&&{};for(i in o)if(o.hasOwnProperty(i))r[i]=o[i]===o?r:dup(o[i]);return r}


Демка: pastehtml.com/view/buikhdvfe.html (чтобы посмотреть без обёртки от pastehtml, замените в ссылке слово «view» на «raw»)

UPD:
Благодаря TheShock функция ещё чуток похудела!
Его варианты:
function dup(o,i,r) {
    r=o;
    if(r && typeof o == "object") {
        r = o instanceof Array ? [] : {};
        for(i in o)
            if(o.hasOwnProperty(i))
                r[i] = dup(o[i]);
    }
    return r
}
// 135
function dup(o,i,r){r=o;if(r&&typeof o=="object"){r=o instanceof Array?[]:{};for(i in o)if(o.hasOwnProperty(i))r[i]=dup(o[i])}return r}
// отказ от ie8, 133 символа:
function dup(o,i,r){r=o;if(r&&typeof o=="object"){r=Array.isArray(o)?[]:{};for(i in o)if(o.hasOwnProperty(i))r[i]=dup(o[i])}return r}

Также поступило дополнительное предложение от mark_ablov как ещё один байт сэкономить, упразднив if:
o.hasOwnProperty(i)?r[i]=dup(o[i]):1


UPD 2:
И окончательное развитие идеи — снова от TheShock:
function c(o,i,r){if(o&&typeof o=="object"){r=o instanceof Array?[]:{};for(i in o)o.hasOwnProperty(i)?r[i]=o[i]===o?r:c(o[i]):0}return r||o}

Ровно 140 байт чистого win'а, и работающего под IE8, и с минимальной проверкой колец!
Ура, товарищи!

Gist: gist.github.com/2369704
Обновлённая демка: pastehtml.com/view/buiv8lzka.html

UPD 3,4: ultimate weapon
Для реального использования сделал ещё одну версию, не влезающую (мягко говоря) в твит, зато работающую с датами и объектными обёртками, а также полностью разруливающую любые циклы и внутренние ссылки. Однострочный вариант занимает 328 байт.
А ещё я понял, что я — чудак редкостный, ибо совсем забыл про проблемы с многофреймовостью и instanceof. Что ж, в боевой версии и это тоже теперь решено.
Gist здесь: gist.github.com/0d3e6ce689e76105f3ef
Демка тут: pastehtml.com/view/bumpwvs4q.html
Теги:
Хабы:
Всего голосов 38: ↑32 и ↓6+26
Комментарии42

Публикации

Истории

Работа

Ближайшие события