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

внешний интерфейс JavaScript

Объекты — это основные блоки JavaScript. Объект — это набор свойств, а свойства — это пары ключ-значение. Почти все объекты в JavaScript находятся на вершине цепочки прототипов.Objectпример.

представлять

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

let obj = {
  a: 1,
  b: 2,
};
let copy = obj;
 
obj.a = 5;
console.log(copy.a);
// 结果 
// copy.a = 5;

—>Edit on JS Bin

objПеременная — это контейнер для инициализации нового объекта.copyПеременная указывает на тот же объект и является ссылкой на этот объект. Итак, теперь есть два способа получить доступ к этому{ a: 1, b: 2, }объект. ты должен пройтиobjпеременная илиcopyПеременные, независимо от того, как вы делаете что-либо с этим объектом, будут влиять на объект.

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

Оригинальный способ копирования объектов

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

function copy(mainObj) {
  let objCopy = {}; // objCopy 将存储 mainObj 的副本
  let key;
 
  for (key in mainObj) {
    objCopy[key] = mainObj[key]; // 将每个属性复制到objCopy对象
  }
  return objCopy;
}
 
const mainObj = {
  a: 2,
  b: 5,
  c: {
    x: 7,
    y: 4,
  },
}
 
console.log(copy(mainObj));

—>Edit on JS Bin

существующие проблемы

  1. objCopyобъект имеет новыйObject.prototypeметод, аналогичныйmainObjОбъекты имеют разные методы-прототипы, а это не то, что нам нужно. Нам нужна точная копия исходного объекта.
  2. Дескрипторы свойств не могут быть скопированы. значениеfalse"Доступно для записи(writable)" дескриптор вobjCopyобъектtrue.
  3. Приведенный выше код только что скопированmainObjперечислимые свойства.
  4. Если свойство в исходном объекте является объектом, то объект будет совместно между копией и исходным объектом, что делает их соответствующие свойства, той и тот же объект.

--- Заметка "Пристань дураков", начало ---

Что касается записываемого свойства в пункте 2:

когдаwritableУстановить какfalseКогда это означает, что оно недоступно для записи, то есть свойство не может быть изменено.

var o = {}; // Creates a new object
 
Object.defineProperty(o, 'a', {
  value: 37,
  writable: false
});
 
console.log(o.a); // logs 37
o.a = 25; // No error thrown
// (it would throw in strict mode,
// even if the value had been the same)
console.log(o.a); // logs 37. The assignment didn't work.
 
// strict mode
(function() {
  'use strict';
  var o = {};
  Object.defineProperty(o, 'b', {
    value: 2,
    writable: false
  });
  o.b = 3; // throws TypeError: "b" is read-only
  return o.b; // returns 2 without the line above
}());

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

Подробно посмотреть:Документация MDN

--- Заметка "Пристань дураков", начало ---

Мелкая копия объекта

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

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

Использование метода Object.assign()

Метод Object.Assign () используется для скопирования всех перечисленных значений атрибута из одного или нескольких исходных объектов в целевой объект.

let obj = {
  a: 1,
  b: 2,
};
let objCopy = Object.assign({}, obj);
console.log(objCopy);
// Result - { a: 1, b: 2 }

—>Edit on JS Bin

слишком далеко. мы создалиobjкопия. Посмотрим, есть ли неизменность:

let obj = {
  a: 1,
  b: 2,
};
let objCopy = Object.assign({}, obj);
 
console.log(objCopy); // result - { a: 1, b: 2 }
objCopy.b = 89;
console.log(objCopy); // result - { a: 1, b: 89 }
console.log(obj); // result - { a: 1, b: 2 }

—>Edit on JS Bin

В приведенном выше коде мы будемobjCopyсвойства в объектахbизменить значение на89, и когда мы логируем измененное в консолиobjCopyобъект, эти изменения касаются толькоobjCopy. Мы видим последнюю строку проверки кодаobjОбъект не был изменен. Это означает, что мы успешно создали копию исходного объекта копии, и на нее нет ссылок.

Подводные камни Object.assign()

Не будь слишком счастлив! Хотя мы успешно создали копию, вроде бы все работает, помните, мы говорили о поверхностной копии? Давайте посмотрим на этот пример:

let obj = {
  a: 1,
  b: {
    c: 2,
  },
}
let newObj = Object.assign({}, obj);
console.log(newObj); // { a: 1, b: { c: 2} }
 
obj.a = 10;
console.log(obj); // { a: 10, b: { c: 2} }
console.log(newObj); // { a: 1, b: { c: 2} }
 
newObj.a = 20;
console.log(obj); // { a: 10, b: { c: 2} }
console.log(newObj); // { a: 20, b: { c: 2} }
 
newObj.b.c = 30;
console.log(obj); // { a: 10, b: { c: 30} }
console.log(newObj); // { a: 20, b: { c: 30} }
 
// 注意: newObj.b.c = 30; 为什么呢..

—>Edit on JS Bin

obj.b.c = 30 ?

ЭтоObject.assign()ловушка.Object.assignПросто мелкая копия.newObj.bа такжеobj.bОба ссылаются на один и тот же объект, а не на отдельную копию, а на копию ссылки на объект. Любые изменения свойств объекта применяются ко всем ссылкам, использующим этот объект. Как мы можем решить эту проблему? Читайте дальше... мы дадим исправление в следующем разделе.

Примечание. Свойства цепочки прототипов и необычные свойства не могут быть скопированы. Смотри сюда:

let someObj = {
  a: 2,
}
 
let obj = Object.create(someObj, { 
  b: {
    value: 2,  
  },
  c: {
    value: 3,
    enumerable: true,  
  },
});
 
let objCopy = Object.assign({}, obj);
console.log(objCopy); // { c: 3 }

—>Edit on JS Bin

someObjвobjцепочка прототипов, поэтому она не будет скопирована.
Атрибутыbявляется неперечислимым свойством.
АтрибутыcИмеет перечисляемый дескриптор свойства, поэтому его можно перечислить. Вот почему его копируют.

объект глубокой копии

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

использовать JSON.parse(JSON.stringify(объект));

Это устраняет проблему, которую мы поднимали ранее. СейчасnewObj.bТам копия вместо ссылки! Это способ глубокого копирования объектов. Вот пример:

let obj = { 
  a: 1,
  b: { 
    c: 2,
  },
}
 
let newObj = JSON.parse(JSON.stringify(obj));
 
obj.b.c = 20;
console.log(obj); // { a: 1, b: { c: 20 } }
console.log(newObj); // { a: 1, b: { c: 2 } } (一个新的对象)

Неизменяемость: ✓

—>Edit on JS Bin

ловушка

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

Метод копирования объекта

Метод — это свойство объекта, который является функцией. В приведенном выше примере у нас еще нет метода для копирования объекта. Теперь давайте попробуем, используя методы, которые мы изучили, для создания копии.

let obj = {
  name: 'scotch.io',
  exec: function exec() {
    return true;
  },
}
 
let method1 = Object.assign({}, obj);
let method2 = JSON.parse(JSON.stringify(obj));
 
console.log(method1); //Object.assign({}, obj)
/* result
{
  exec: function exec() {
    return true;
  },
  name: "scotch.io"
}
*/
 
console.log(method2); // JSON.parse(JSON.stringify(obj))
/* result
{
  name: "scotch.io"
}
*/

—>Edit on JS Bin

Результаты показывают,Object.assign()методы, которые можно использовать для копирования объектов, используяJSON.parse(JSON.stringify(obj))нет.

Копировать объекты круговой ссылки

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

Используйте JSON.parse(JSON.stringify(объект))

попробуем использоватьJSON.parse(JSON.stringify(object)):

// 循环引用对象
let obj = { 
  a: 'a',
  b: { 
    c: 'c',
    d: 'd',
  },
}
 
obj.c = obj.b;
obj.e = obj.a;
obj.b.c = obj.c;
obj.b.d = obj.b;
obj.b.e = obj.b.c;
 
let newObj = JSON.parse(JSON.stringify(obj));
 
console.log(newObj); 

—>Edit on JS Bin

оказаться:

Это понятно,JSON.parse(JSON.stringify(object))Нельзя использовать для копирования объектов круговой ссылки.

Используйте Object.assign()

попробуем использоватьObject.assign():

// 循环引用对象
let obj = { 
  a: 'a',
  b: { 
    c: 'c',
    d: 'd',
  },
}
 
obj.c = obj.b;
obj.e = obj.a;
obj.b.c = obj.c;
obj.b.d = obj.b;
obj.b.e = obj.b.c;
 
let newObj2 = Object.assign({}, obj);
 
console.log(newObj2); 

—>Edit on JS Bin

оказаться:

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

Используйте оператор спреда (…)

В ES6 уже есть элемент rest для назначения деструктурирования массива и реализованный оператор для расширения литерала массива. Взгляните на реализацию оператора распространения для массивов здесь:

const array = [
  "a",
  "c",
  "d", {
    four: 4
  },
];
const newArray = [...array];
console.log(newArray);
// 结果 
// ["a", "c", "d", { four: 4 }]

—>Edit on JS Bin

Оператор распространения для литералов объектов в настоящее времяПредложение фазы 3 для ECMAScript. Оператор распространения литерала объекта копирует перечисляемые свойства из исходного объекта в целевой объект. В следующем примере показано, как легко скопировать объект после того, как предложение было принято.

let obj = {
  one: 1,
  two: 2,
}
 
let newObj = { ...z };
 
// { one: 1, two: 2 }

Примечание. Это будет действительная только неглубокая копия.

В заключение

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

Оригинальная ссылка:Scotch.io/put people-talk/co…

Связанное Чтение