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

Как глубоко клонировать объект JavaScript

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

Копирование объектов в JavaScript может быть сложным. Некоторые способы выполняют поверхностное копирование, что в большинстве случаев является поведением по умолчанию.

  • Глубокая копия против мелкой копии
  • Самый простой вариант: используйте Lodash
  • Объект.назначить()
  • Использование оператора распространения объекта
  • Неправильные решения
    • Использование Object.create()
    • Сериализация JSON

Глубокая копия против мелкой копии

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

Если объект ссылается на другие объекты, то при выполнении неглубокой копии объекта вы копируете ссылки на внешние объекты.

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

В поисках того, как глубоко клонировать объект в JavaScript в Интернете, вы найдете множество ответов, но ответы не всегда правильные .

Самый простой вариант: используйте Lodash

Мое предложение выполнить глубокое копирование – полагаться на хорошо протестированную, очень популярную и тщательно поддерживаемую библиотеку: Lodash.

Lodash предлагает очень удобный клон и глубокое клонирование функции для выполнения неглубокого и глубокого клонирования.

У Lodash есть эта приятная функция: вы можете импортировать отдельные функции отдельно в свой проект, чтобы значительно уменьшить размер зависимости.

В Node.js:

const clone = require('lodash.clone')
const clonedeep = require('lodash.clonedeep')

Вот пример, в котором показаны эти две используемые функции:

const clone = require('lodash.clone')
const clonedeep = require('lodash.clonedeep')

const externalObject = {
  color: 'red',
}

const original = {
  a: new Date(),
  b: NaN,
  c: new Function(),
  d: undefined,
  e: function () {},
  f: Number,
  g: false,
  h: Infinity,
  i: externalObject,
}

const cloned = clone(original)

externalObject.color = 'blue'

console.info('⬇️ shallow cloning 🌈')
console.info(
  '✏️ Notice the i.color property we changed on original is also changed in the shallow copy'
)
console.log(original)
console.log(cloned)

const deepcloned = clonedeep(original)

externalObject.color = 'yellow'
console.log('')
console.info('⬇️ deep cloning 🌈')
console.info('✏️ Notice the i.color property does not propagate any more')
console.log(original)
console.log(deepcloned)

В этом простом примере мы сначала создаем неглубокую копию и редактируем свойство i.color, которое распространяется на скопированный объект.

В глубоком клоне этого не происходит.

Объект.назначить()

Object.assign() выполняет поверхностную копию объекта, а не глубокий клон.

const copied = Object.assign({}, original)

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

const original = {
  name: 'Fiesta',
  car: {
    color: 'blue',
  },
}
const copied = Object.assign({}, original)

original.name = 'Focus'
original.car.color = 'yellow'

copied.name //Fiesta
copied.car.color //yellow

Использование оператора распространения объекта

Оператор распространения – это функция ES6/ES2015, которая обеспечивает очень удобный способ выполнения неглубокого клонирования, эквивалентный тому, что делает Object.assign().

const copied = { ...original }

Неправильные решения

В Интернете вы найдете множество предложений. Вот некоторые неправильные:

Использование Object.create()

Примечание: не рекомендуется

const copied = Object.create(original)

Это неправильно, он не выполняет никакой копии.

Вместо этого исходный объект используется в качестве прототипа | скопированного .

По-видимому, это работает, но под колпаками это не так:

const original = {
  name: 'Fiesta',
}
const copied = Object.create(original)
copied.name //Fiesta

original.hasOwnProperty('name') //true
copied.hasOwnProperty('name') //false

Подробнее см. в разделе Object.create().

Сериализация JSON

Примечание: не рекомендуется

Некоторые рекомендуют преобразовать в JSON:

const cloned = JSON.parse(JSON.stringify(original))

но это имеет неожиданные последствия.

Сделав это, вы потеряете любое свойство Javascript, которое не имеет эквивалентного типа в JSON, например Функция или Бесконечность . Любое свойство, назначенное неопределенному , будет игнорироваться JSON.stringify , в результате чего они будут пропущены в клонированном объекте.

Кроме того, некоторые объекты преобразуются в строки, такие как, например, объекты данных (также без учета часового пояса и по умолчанию в UTC), Набор, Карта и многие другие:

JSON.parse(
  JSON.stringify({
    a: new Date(),
    b: NaN,
    c: new Function(),
    d: undefined,
    e: function () {},
    f: Number,
    g: false,
    h: Infinity,
  })
)

Это работает только в том случае, если у вас нет никаких внутренних объектов и функций, а только значения.

Оригинал: “https://flaviocopes.com/how-to-clone-javascript-object/”