Рубрики
Без рубрики

Как скопировать объекты в JavaScript

Автор оригинала: Scott Robinson.

Вступление

Очень распространенная задача в программировании, независимо от языка, является копирование (или клон) объектом по значению, в отличие от копирования посредством ссылки. Разница в том, что при копировании по значению у вас есть два не связанный объекты с одинаковым значением или данными. Копирование посредством ссылки означает, что у вас есть два объекта, которые указывают на те же данные в памяти. Это означает, что если вы манипулируете объектом A, например, он также будет манипулировать объектом B, поскольку они оба ссылаются на одни и те же основные данные.

В этой статье я пройду несколько способов скопировать объекты по значению в JavaScript. Я покажу, как вы можете сделать это, используя обе сторонние библиотеки, так и путем написания собственной функции копирования.

Примечание : Поскольку Node.js – это просто время выполнения, построенной на двигателе V8 JavaScript, все методы клонов, которые я показываю в этой статье, также будет работать для узла.

Сторонние библиотеки

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

Лоташ

Лоташ Библиотека предоставляет несколько различных методов копирования или клонирования, объектов в зависимости от вашего использования.

Самый общий метод является клон () Метод, который обеспечивает неглубокий Копии объектов. Он работает, просто передавая объект как первый аргумент, и копия будет возвращена:

const _ = require('lodash');

let arrays = {first: [1, 2, 3], second: [4, 5, 6]};
let copy = _.clone(arrays);
console.log(copy);
{ first: [ 1, 2, 3 ], second: [ 4, 5, 6 ] }

Это означает, что объект «верхнего уровня» (или массив, буфер, карта и т. Д.) Клонируется, но любые более глубокие объекты будут скопированы посредством ссылки. Подведенный ниже код демонстрирует, что Первый Массив в оригинале Массивы Объект – это тот же объект, что и Первый Массив в Скопировать объект:

const _ = require('lodash');

let arrays = {first: [1, 2, 3], second: [4, 5, 6]};
let copy = _.clone(arrays);
console.log(copy.first === arrays.first);
true

Если вы предпочитаете, что Все Объекты, как мелкие, так и глубокие объекты, скопированы, то вы захотите использовать CLONDEEP () Метод вместо этого:

const _ = require('lodash');

let arrays = {first: [1, 2, 3], second: [4, 5, 6]};
let copy = _.cloneDeep(arrays);
console.log(copy);
{ first: [ 1, 2, 3 ], second: [ 4, 5, 6 ] }

Этот метод работает рекурсивно клонированием всех значений на любом уровне глубины.

На проведении одинакового чека на равенство сверху, мы видим, что оригинальные и скопированные массивы больше не равны, так как они являются уникальными копиями:

const _ = require('lodash');

let arrays = {first: [1, 2, 3], second: [4, 5, 6]};
let copy = _.cloneDeep(arrays);
console.log(copy.first === arrays.first);
false

Лоташ предлагает еще несколько методов клонов, в том числе клоневит () и CLONDEEPWITH () Отказ Оба эти методы принимают другой параметр под названием Customizer , который является функцией, используемой для создания скопированного значения.

Поэтому, если вы хотите использовать какую-либо пользовательскую логику копирования, вы можете передавать функцию для обработки его в методе Lodyash. Например, скажем, у вас есть объект, который содержит некоторые Дата Объекты, но вы хотите, чтобы те, которые были преобразованы в Timestamps после копирования, вы можете сделать это так:

const _ = require('lodash');

let tweet = {
    username: '@ScottWRobinson',
    text: 'I didn\'t actually tweet this',
    created_at: new Date('December 21, 2018'),
    updated_at: new Date('January 01, 2019'),
    deleted_at: new Date('February 28, 2019'),
};
let tweetCopy = l.cloneDeepWith(tweet, (val) => {
    if (l.isDate(val)) {
        return val.getTime();
    }
});
console.log(tweetCopy);
{ username: '@ScottWRobinson',
  text: 'I didn\'t actually tweet this',
  created_at: 1545372000000,
  updated_at: 1546322400000,
  deleted_at: 1551333600000 }

Как вы можете видеть, единственные данные, которые были изменены нашим методом, были теми, что были Дата Объекты, которые сейчас были включены в метки Unix.

Нижнее подчеркивание

Подчеркивание клон () Способ работает так же, как и Лодаш клон () метод. Он предоставляет только неглубокую копию данного объекта, причем какие-либо вложенные объекты копируются посредством ссылки.

Тот же пример, что и прежде чем демонстрирует это:

const _ = require('underscore');

let arrays = {first: [1, 2, 3], second: [4, 5, 6]};
let copy = _.clone(arrays);
console.log(copy.first === arrays.first);
true

К сожалению, библиотека подчеркивания, похоже, не имеет никакого метода для обработки глубокого копирования. Вы можете реализовать эту логику самостоятельно (используя некоторые из логики, показанной ниже) и все еще используйте подчеркивание клон Способ для мелкой копии, или вы можете попробовать одно из других решений в этой статье.

Пользовательские решения

Как я уже упоминал ранее, взяв на себя эту проблему самостоятельно, – это сложный, поскольку есть много случаев (и сложных краевых случаев) для обработки при клонировании объекта в JavaScript. Хотя, если сделано правильно, вы сможете добавить немного хорошей настройки в вашем методе, который может быть невозможен иначе.

Используя JSON методы

Одно часто цитируемое решение – просто использовать Json.stringify и Json.parse Методы для вашего преимущества, как это:

let arrays = {first: [1, 2, 3], second: [4, 5, 6]};
let copy = JSON.parse(JSON.stringify(arrays));
console.log(copy);
{ first: [ 1, 2, 3 ], second: [ 4, 5, 6 ] }

Это оставит вас с глубоко скопированным объектом, и он работает очень хорошо для простых объектов, которые легко преобразуются в JSON.

Мы можем снова проверить это, используя тот же чек, как свыше:

console.log(copy.first === arrays.first);
false

Если вы знаете, что ваш объект легко сериализуется, то это может быть хорошим решением для вас.

Написание собственного с нуля

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

Поскольку я не доверяю себе правильно реализовать полный метод клона (и рисковать читателями, копируя мои ошибки в своем производстве), я скопировал следующую функцию клона из этот гид , который копирует объекты рекурсивно и кажется, работают над многими общими типами данных, которые вы получите в JavaScript.

function clone(thing, opts) {
    var newObject = {};
    if (thing instanceof Array) {
        return thing.map(function (i) { return clone(i, opts); });
    } else if (thing instanceof Date) {
        return new Date(thing);
    } else if (thing instanceof RegExp) {
        return new RegExp(thing);
    } else if (thing instanceof Function) {
        return opts && opts.newFns ? new Function('return ' + thing.toString())() : thing;
    } else if (thing instanceof Object) {
        Object.keys(thing).forEach(function (key) { newObject[key] = clone(thing[key], opts); });
        return newObject;
    } else if ([ undefined, null ].indexOf(thing) > -1) {
        return thing;
    } else {
        if (thing.constructor.name === 'Symbol') {
            return Symbol(thing.toString().replace(/^Symbol\(/, '').slice(0, -1));
        }
        return thing.__proto__.constructor(thing);
    }
}

Эта функция работает путем обработки конкретных случаев при необходимости (например, массивы, регулярные выражения, функции и т. Д.), а затем для всех других типов данных (например, цифры, строки, логические значения и т. Д.) Это по умолчанию для вещь Собственный конструктор для копирования значения. Если вещь это сам объект, то он просто рекурсивно называет себя на детских атрибутах вещь Отказ

Ознакомьтесь с полным циклом в ссылке выше для всех типов данных и кромки, которые оно было проверено.

Заключение

Хотя в теории простой, на практике копирование объекта в JavaScript – это что-то, кроме простых. К счастью, там есть довольно много решений для вас, например Cloneeep в Лоташ или даже встроенном JSON методы. И если по какой-либо причине никто из них подходит, тогда это это Можно написать свой собственный метод клона, пока вы тщательно проверяете его.

Удачи, и мы знаем, если у вас есть какие-либо мысли, идеи или советы в комментариях.