пятница, 20 сентября 2013 г.

Клонирование объекта в JavaScript, или Классика велосипедостроения...

Честно говоря, непонятно, почему до сих пор в стандарт языка ECMAScript не внесены две часто нужные вещи: клонирование объекта и «наложение» одного объекта на другой. Помимо прочего, полноценная реализация этих методов возможна только на уровне движка, но нет же — приходится довольствоваться скриптовыми велосипедами от различных библиотек или писать свой.

Ниже мой вариант с пояснениями.

Сначала о клонировании, с ним проще. Во-первых, клонировать вложенные объекты или копировать ссылки — может различаться от случая к случаю, поэтому я ввел параметр deep; если его не указать, undefined будет интерпретировано как логическая «ложь», т.е. вариант по умолчанию — вложенные объекты не клонируются, исключение — массивы, при клонировании которых их элементы клонируются всегда. Во-вторых, учитываем прототип (сработает не во всех браузерах) и функцию-конструктор, что может оказаться важным (хотя и нечасто). В-третьих, не пытаемся клонировать функции — это нельзя сделать, судя по всему, ни в одном движке.

Варианты через uneval()/toSource() и eval(), а также посредством сериализации десериализации JSON, были отметены: первый — из-за работы только в одном браузере, второй — поскольку накладывает ограничения на значения свойств.

Теперь о «слиянии», «наложении»... не могу точно сформулировать, в общем — метод merge(). Этот метод копирует свойства объекта-источника в текущий. При этом если свойства сами являются объектами, то к ним он также рекурсивно применяется. Клонирование свойств при этом не выполняется, если оно необходимо, можно использовать что-то вроде «dest.merge(src.clone(true))».

Второй, необязательный, параметр — объект с двумя возможными свойствами-флагами, т.е. значениями булевского типа (точнее, любыми значениями, которые будут приведены к булевскому в первых двух строчках метода):

filter

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

Зачем это надо? Одна из частых практик использования подобных методов, например — это наложение на какую-то впоследствии визуализируемую структуру перевода, взятого из другого источника. Очевидно, что если перевод содержит лишние элементы, их следует проигнорировать, поскольку они всё равно не содержат других необходимых свойств, кроме отображаемого текста.

ids

Читать как «идентификаторы». Этот хитрый флаг позволяет обрабатывать элементы массивов с установленным свойством id так же, как свойства объектов с соответствующим идентификатором. Что из этого получается, лучше пояснить на примере:

Даст следующий результат:

В завершение замечу, что весь вышеизложенный код писался исключительно под себя и не представляет собою какой-либо публичной и поддерживаемой библиотеки.

3 комментария:

  1. А тебя не смущает злобность изменения Object.prototype? ИМХО, пустому объекту лучше всё же оставаться пустым.

    ОтветитьУдалить
    Ответы
    1. А он не особо-то пустой. У него есть свойства типа constructor, toString... Так что я хоть и настороженно смотрю на такие вещи, особого страха не испытываю. Конкретно эти методы вполне имеют право там находиться.

      Другой вопрос, а не сломается ли чего, когда соответствующими функциями таки озадачатся разрабочики браузеров или стандарта языка... Сломается, как пить дать — это вообще основной аргумент против monkey-patching'а в JS. Но на этот счет есть последний абзац :)

      Удалить
    2. Короче: смущает, но не сильно.

      Удалить