JavaScript метод assign()ECMAScript 2015

JavaScript Object

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

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


Обращаю Ваше внимание, что метод .assign()ECMAScript 2015 перезаписывает значение свойства или метода в целевом объекте, если они имеют один и тот же ключ, что и в переданном источнике. Если источники имеют один и тот же ключ (свойство), то более поздние источники будут перезаписывать более ранние.


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

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

МетодChrome
Chrome
Firefox
Firefox
Opera
Opera
Safari
Safari
Internet Explorer
IExplorer
Microsoft Edge
Edge
assign()ECMAScript 201545.034.032.09.0НетДа

JavaScript синтаксис:

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

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

Версия JavaScript

ECMAScript 2015 (6th Edition, ECMA-262)

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

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

Исключения

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

Пример использования

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

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

// инициализируем переменные, содержащие объект
let a = {a: 1},
    b = {b: 2},
    c = {c: 3};

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

console.log( mergeObjects ); // переменная содержит значение { a: 1, b: 2, c: 3 }
console.log( a ); // переменная содержит значение { a: 1, b: 2, c: 3 }

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

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

// инициализируем переменные, содержащие объект с одноименными свойствами
let a = {x: 1},
    b = {x: 2},
    c = {x: 3};

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

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

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

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

let student = {name: "Ivan", family: "Susanin"};

let shallowClone = Object.assign( {}, student );
console.log( shallowClone ); // переменная содержит значение {name: "Ivan", family: "Susanin"}

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

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

// инициализируем переменную, содержащую объект
let obj = {
    a: 1,
    b: {
      c: 2
    }
};

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

obj.a = 100;            // изменяем значение свойства объекта obj
newObj.a = 150;         // изменяем значение свойства объекта newObj
console.log( obj );     // переменная содержит значение { a: 100, b: { c: 2} }
console.log( newObj );  // переменная содержит значение { a: 150, b: { c: 2} }

obj.b.c = 200;          // изменяем значение свойства объекта obj
console.log( obj );     // переменная содержит значение { a: 100, b: { c: 200} }
console.log( newObj );  // переменная содержит значение { a: 150, b: { c: 200} }   

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

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

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

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

// инициализируем переменную, содержащую объект
let obj = {
    a: 1,
    b: {
      c: 2
    }
};

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

obj.b.c = 200;          // изменяем значение свойства объекта obj
console.log( obj );     // переменная содержит значение { a: 100, b: { c: 200} }
console.log( newObj );  // переменная содержит значение { a: 150, b: { c: 2} }   

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

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

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

// инициализируем переменную, содержащую объект
let obj = {
    [Symbol( "test" )]: "test",
    "name": "Vasya"
};

let newObj = Object.assign( {}, obj ) ;
console.log( obj ); // переменная содержит значение {name: "Vasya", Symbol(test): "test"}

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

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

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); 
console.log( obj ); // переменная содержит значение {0: "o", 1: "k", 2: "?"}

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

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

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

// задаем пустому объекту новое свойство и инициализируем переменную
let target = Object.defineProperty( {}, "a", { 
    value: 1,       // значение свойства
    writable: false // свойство только для чтения
}); 

console.log( target );  // переменная содержит значение {a: 1}

Object.assign( target, { b: 2 }, { c: 3, a: 4, d: 5 }); // TypeError свойство "a" только для чтения

console.log( target );  // переменная содержит значение {b: 2, c: 3, a: 1}

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

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

JavaScript Object