JavaScript метод Object.assign()

Определение и применение

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

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

Object.assign(target) допустим без источников. null и undefined в качестве цели вызывают TypeError. Копируются значения, а не дескрипторы: чтение вызывает геттеры источника, запись — сеттеры цели. for...in перечисляет только строковые ключи, хотя assign() также копирует перечислимые символы. JSON-клонирование подходит только для совместимых с JSON данных: оно теряет функции и часть значений, преобразует даты в строки и не поддерживает циклы и BigInt. Для поддерживаемых типов можно использовать structuredClone().

Поддержка браузерами

Спецификация: ecma262

Поддерживается во всех основных браузерах с 2016 года.

Показать браузеры
Совместимостьjavascript.builtins.Object.assign
ВозможностьКомпьютерыМобильные
ChromeEdgeFirefoxOperaSafariChrome AndroidFirefox for AndroidOpera AndroidSafari on iOSSamsung BrowserWebView AndroidWebView on iOS
assign✓45✓12✓34✓32✓9✓45✓34✓32✓9✓5.0✓45✓9

поддерживаетсяс ограниченияминет поддержкинет данных≤31 — не позднее версии 31

JavaScript синтаксис

JavaScript
// копирование из одного исходного объекта
Object.assign( target, source );

// копирование из нескольких исходных объектов
Object.assign( target, ...sources );

Спецификация

ECMAScript: Object.assign()

Значения параметров

Object: справочные сведения
Параметр Описание
target Целевой объект в который будут скопированы значения всех перечислимых собственных свойств и методов объекта, или объектов, перечисленных в параметре метода. Обязательное значение.
source / sources Исходный объект или объекты из которых производиться копирование значений всех перечислимых собственных свойств и методов. Необязательные параметры.
Такие источники как null или undefined обрабатываются как пустые объекты и не добавляют ничего к целевому объекту. Если источник не имеет перечислимых свойств или методов, то он также не будет добавлен к целевому объекту.

Исключения

Object: справочные сведения
Тип исключения Описание
TypeError Возникает в том случае, если целевой объект равен null или undefined, а также если целевое свойство или метод перезаписывается, а оно доступно только для чтения, целевой объект при этом может быть изменен, если какие-либо свойства или методы были добавлены до возникновения подобной ошибки (пример рассмотрен ниже).

Примеры, попробуйте сами

Слияние объектов

В этом примере мы рассмотрим как с помощью метода assign() поверхностно скопировать собственные свойства трех объектов в один объект:

В примере «Слияние трёх объектов» показан результат вызова метода.

Слияние трёх объектов

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

index.html
HTML
<!doctype html>
<html lang="ru">
  <head>
    <meta charset="UTF-8" />
    <title>Слияние трёх объектов</title>
  </head>
  <body>
    <p>Результат выполнения выводится в консоль.</p>
  </body>
</html>
main.js
JavaScript
// инициализируем переменные, содержащие объект
let a = { a: 1 },
    b = { b: 2 },
    c = { c: 3 }

let mergeObjects = Object.assign(a, b, c) // переменная "a" является целевым объектом, а переменные "b" и "c" служат источниками

try {
    console.log(mergeObjects)
} catch (error) {
    console.log(error.name + ': ' + error.message) // Показываем ошибку и продолжаем сравнение.
} // переменная содержит значение { a: 1, b: 2, c: 3 }
try {
    console.log(a)
} catch (error) {
    console.log(error.name + ': ' + error.message) // Показываем ошибку и продолжаем сравнение.
} // переменная содержит значение { a: 1, b: 2, c: 3 }

Слияние объектов с одинаковыми свойствами

В этом примере мы рассмотрим с Вами как происходит поверхностное копирование объектов, которые имеют одинаковые свойства:

В примере «Перезапись совпадающих ключей» показан результат вызова метода.

Перезапись совпадающих ключей

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

index.html
HTML
<!doctype html>
<html lang="ru">
  <head>
    <meta charset="UTF-8" />
    <title>Перезапись совпадающих ключей</title>
  </head>
  <body>
    <p>Результат выполнения выводится в консоль.</p>
  </body>
</html>
main.js
JavaScript
// инициализируем переменные, содержащие объект с одноименными свойствами
let a = { x: 1 },
    b = { x: 2 },
    c = { x: 3 }

try {
    Object.assign(a, b, c)
} catch (error) {
    console.log(error.name + ': ' + error.message) // Показываем ошибку и продолжаем сравнение.
} // переменная "a" является целевым объектом, а переменные "b" и "c" служат источниками
try {
    console.log(a)
} catch (error) {
    console.log(error.name + ': ' + error.message) // Показываем ошибку и продолжаем сравнение.
} // переменная содержит значение { x: 3 }

Обратите внимание, что метод .assign() перезаписывает значение свойства в целевом объекте, если они имеют один и тот же ключ, что и в переданном источнике. Если источники имеют один и тот же ключ (свойство), то более поздние источники будут перезаписывать более ранние, как в этом примере, сначала переменная "b" перезаписала значение свойства x переменной "a", а потом переменная "c" перезаписала значение свойства x переменной "a".

Поверхностное копирование объекта

В следующем примере мы рассмотрим как произвести поверхностное копирование объекта в новый объект:

В примере «Копирование в новый объект» показан результат вызова метода.

Копирование в новый объект

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

index.html
HTML
<!doctype html>
<html lang="ru">
  <head>
    <meta charset="UTF-8" />
    <title>Копирование в новый объект</title>
  </head>
  <body>
    <p>Результат выполнения выводится в консоль.</p>
  </body>
</html>
main.js
JavaScript
let student = { name: 'Ivan', family: 'Susanin' }

let shallowClone = Object.assign({}, student)
try {
    console.log(shallowClone)
} catch (error) {
    console.log(error.name + ': ' + error.message) // Показываем ошибку и продолжаем сравнение.
} // переменная содержит значение {name: "Ivan", family: "Susanin"}

Проблемы при поверхностном копировании

В этом примере мы с Вами детально рассмотрим как происходит поверхностное копирование элементов объекта, значением которых является объект:

В примере «Общие вложенные объекты» показан результат вызова метода.

Общие вложенные объекты

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

index.html
HTML
<!doctype html>
<html lang="ru">
  <head>
    <meta charset="UTF-8" />
    <title>Общие вложенные объекты</title>
  </head>
  <body>
    <p>Результат выполнения выводится в консоль.</p>
  </body>
</html>
main.js
JavaScript
// инициализируем переменную, содержащую объект
let obj = {
    a: 1,
    b: {
        c: 2,
    },
}

// осуществляем поверхностное копирование объекта и инициализируем переменную
let newObj = Object.assign({}, obj)
try {
    console.log(newObj)
} catch (error) {
    console.log(error.name + ': ' + error.message) // Показываем ошибку и продолжаем сравнение.
} // переменная содержит значение { a: 1, b: { c: 2} }

try {
    obj.a = 100
} catch (error) {
    console.log(error.name + ': ' + error.message) // Показываем ошибку и продолжаем сравнение.
} // изменяем значение свойства объекта obj
try {
    newObj.a = 150
} catch (error) {
    console.log(error.name + ': ' + error.message) // Показываем ошибку и продолжаем сравнение.
} // изменяем значение свойства объекта newObj
try {
    console.log(obj)
} catch (error) {
    console.log(error.name + ': ' + error.message) // Показываем ошибку и продолжаем сравнение.
} // переменная содержит значение { a: 100, b: { c: 2} }
try {
    console.log(newObj)
} catch (error) {
    console.log(error.name + ': ' + error.message) // Показываем ошибку и продолжаем сравнение.
} // переменная содержит значение { a: 150, b: { c: 2} }

try {
    obj.b.c = 200
} catch (error) {
    console.log(error.name + ': ' + error.message) // Показываем ошибку и продолжаем сравнение.
} // изменяем значение свойства объекта obj
try {
    console.log(obj)
} catch (error) {
    console.log(error.name + ': ' + error.message) // Показываем ошибку и продолжаем сравнение.
} // переменная содержит значение { a: 100, b: { c: 200} }
try {
    console.log(newObj)
} catch (error) {
    console.log(error.name + ': ' + error.message) // Показываем ошибку и продолжаем сравнение.
} // переменная содержит значение { a: 150, b: { c: 200} }

Важным в понимании работы метода assign() и поверхностного копирования в частности является то, что если исходное значение свойство является ссылкой на объект, то оно копирует только ссылку на это значение, а не создает новое значение. Независимо от того где мы изменяем это значение - в новом объекте, или в первоначальном, значение будет изменено везде. Для осуществления глубокого клонирования нам необходимо использовать другие альтернативы.

Глубокое копирование объекта

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

Давайте рассмотрим одно из решений проблемы, с которой мы столкнулись с помощью метода assign() и сделаем глубокую копию нашего объекта:

В примере «Копирование совместимых с JSON данных» показан результат вызова метода.

Копирование совместимых с JSON данных

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

index.html
HTML
<!doctype html>
<html lang="ru">
  <head>
    <meta charset="UTF-8" />
    <title>Копирование совместимых с JSON данных</title>
  </head>
  <body>
    <p>Результат выполнения выводится в консоль.</p>
  </body>
</html>
main.js
JavaScript
// инициализируем переменную, содержащую объект
let obj = {
    a: 1,
    b: {
        c: 2,
    },
}

// осуществляем глубокое копирование объекта и инициализируем переменную
let newObj = JSON.parse(JSON.stringify(obj))
try {
    console.log(newObj)
} catch (error) {
    console.log(error.name + ': ' + error.message) // Показываем ошибку и продолжаем сравнение.
} // переменная содержит значение { a: 1, b: { c: 2} }

try {
    obj.b.c = 200
} catch (error) {
    console.log(error.name + ': ' + error.message) // Показываем ошибку и продолжаем сравнение.
} // изменяем значение свойства объекта obj
try {
    console.log(obj)
} catch (error) {
    console.log(error.name + ': ' + error.message) // Показываем ошибку и продолжаем сравнение.
} // переменная содержит значение { a: 1, b: { c: 200} }
try {
    console.log(newObj)
} catch (error) {
    console.log(error.name + ': ' + error.message) // Показываем ошибку и продолжаем сравнение.
} // переменная содержит значение { a: 1, b: { c: 2} }

В этом примере с использованием метода JSON.stringify мы преобразовали значение переменной obj в строку JSON, а с помощью метода JSON.parse парсим (разбираем) эту строку и возвращаем новый объект, соответствующий переданной строке. В результате чего, свойства, значением которых является объект имеют собственные значения, а не ссылаются на первоначальный объект. Благодаря этим преобразованиям мы получаем новый объект, который является независимой копией данных этого примера.

Копирование символов

Метод assign() позволяет скопировать свойства, ключом которых является символ (Symbol):

В примере «Копирование символьного ключа» показан результат вызова метода.

Копирование символьного ключа

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

index.html
HTML
<!doctype html>
<html lang="ru">
  <head>
    <meta charset="UTF-8" />
    <title>Копирование символьного ключа</title>
  </head>
  <body>
    <p>Результат выполнения выводится в консоль.</p>
  </body>
</html>
main.js
JavaScript
// инициализируем переменную, содержащую объект
let obj = {
    [Symbol('test')]: 'test',
    name: 'Vasya',
}

let newObj = Object.assign({}, obj)
try {
    console.log(obj)
} catch (error) {
    console.log(error.name + ': ' + error.message) // Показываем ошибку и продолжаем сравнение.
} // {name: "Vasya"} — вывод примера не показывает символьные ключи, поэтому они выведены ниже

const keys = Object.getOwnPropertySymbols(newObj)
console.log(keys.map(String))
console.log(newObj[keys[0]])

Нюансы поверхностного копирования

В этом примере мы с Вами рассмотрим некоторые нюансы при поверхностном копировании:

В примере «Примитивы в источниках» показан результат вызова метода.

Примитивы в источниках

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

index.html
HTML
<!doctype html>
<html lang="ru">
  <head>
    <meta charset="UTF-8" />
    <title>Примитивы в источниках</title>
  </head>
  <body>
    <p>Результат выполнения выводится в консоль.</p>
  </body>
</html>
main.js
JavaScript
let v1 = null,
    v2 = true,
    v3 = undefined,
    v4 = NaN,
    v5 = '',
    v6 = Symbol('test'),
    v7 = [],
    v8 = {},
    v9 = Infinity,
    v10 = 100,
    v11 = 'ok?'

let obj = Object.assign({}, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11)
try {
    console.log(obj)
} catch (error) {
    console.log(error.name + ': ' + error.message) // Показываем ошибку и продолжаем сравнение.
} // переменная содержит значение {0: "o", 1: "k", 2: "?"}

Обратите внимание, что только строки могут иметь собственные перечислимые свойства, остальные переменные просто были проигнорированы.

Возникновение исключения

Давайте рассмотрим пример в котором рассмотрим при каких условиях вызывается исключение при работе со свойством assign():

В примере «Ошибка записи и частичный результат» показан результат вызова метода.

Ошибка записи и частичный результат

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

index.html
HTML
<!doctype html>
<html lang="ru">
  <head>
    <meta charset="UTF-8" />
    <title>Ошибка записи и частичный результат</title>
  </head>
  <body>
    <p>Результат выполнения выводится в консоль.</p>
  </body>
</html>
main.js
JavaScript
// задаем пустому объекту новое свойство и инициализируем переменную
let target = Object.defineProperty({}, 'a', {
    value: 1, // значение свойства
    writable: false, // свойство только для чтения
})

try {
    console.log(target)
} catch (error) {
    console.log(error.name + ': ' + error.message) // Показываем ошибку и продолжаем сравнение.
} // {} — свойство a неперечислимое и в выводе не видно
console.log(target.a) // 1

try {
    Object.assign(target, { b: 2 }, { c: 3, a: 4, d: 5 })
} catch (error) {
    console.log(error.name + ': ' + error.message) // Показываем ошибку и продолжаем сравнение.
} // TypeError свойство "a" только для чтения

try {
    console.log(target)
} catch (error) {
    console.log(error.name + ': ' + error.message) // Показываем ошибку и продолжаем сравнение.
} // {b: 2, c: 3}: свойства до ошибки скопированы, d — нет
console.log(target.a, 'd' in target) // 1 false

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

Обратите внимание, что те свойства, которые были добавлены до ошибки, которая прервала копирование, были успешно скопированы в целевой объект.