Как стать автором
Поиск
Написать публикацию
Обновить

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

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

Не знаю, будет ли кому интересно, но соорудил сегодня такое чудо: 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

Публикации

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