Объекты — это основные блоки JavaScript. Объект — это набор свойств, а свойства — это пары ключ-значение. Почти все объекты в JavaScript находятся на вершине цепочки прототипов.Object
пример.
представлять
Как вы знаете, оператор присваивания не создает копию объекта, он просто присваивает ссылку, давайте посмотрим на следующий код:
let obj = {
a: 1,
b: 2,
};
let copy = obj;
obj.a = 5;
console.log(copy.a);
// 结果
// copy.a = 5;
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));
существующие проблемы
-
objCopy
объект имеет новыйObject.prototype
метод, аналогичныйmainObj
Объекты имеют разные методы-прототипы, а это не то, что нам нужно. Нам нужна точная копия исходного объекта. - Дескрипторы свойств не могут быть скопированы. значение
false
"Доступно для записи(writable
)" дескриптор вobjCopy
объектtrue
. - Приведенный выше код только что скопирован
mainObj
перечислимые свойства. - Если свойство в исходном объекте является объектом, то объект будет совместно между копией и исходным объектом, что делает их соответствующие свойства, той и тот же объект.
--- Заметка "Пристань дураков", начало ---
Что касается записываемого свойства в пункте 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 }
слишком далеко. мы создали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 }
В приведенном выше коде мы будем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; 为什么呢..
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 }
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 } } (一个新的对象)
Неизменяемость: ✓
ловушка
К сожалению, этот метод нельзя использовать для репликации пользовательских методов объекта. следующим образом.
Метод копирования объекта
Метод — это свойство объекта, который является функцией. В приведенном выше примере у нас еще нет метода для копирования объекта. Теперь давайте попробуем, используя методы, которые мы изучили, для создания копии.
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"
}
*/
Результаты показывают,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);
оказаться:
Это понятно,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);
оказаться:
Object.assign()
Работает для мелких копий круговых справочных объектов, но не для глубоких копий. Не стесняйтесь просматривать дерево кругового эталонного объекта на консоли браузера. Я уверен, что вы найдете много интересной работы.
Используйте оператор спреда (…)
В ES6 уже есть элемент rest для назначения деструктурирования массива и реализованный оператор для расширения литерала массива. Взгляните на реализацию оператора распространения для массивов здесь:
const array = [
"a",
"c",
"d", {
four: 4
},
];
const newArray = [...array];
console.log(newArray);
// 结果
// ["a", "c", "d", { four: 4 }]
Оператор распространения для литералов объектов в настоящее времяПредложение фазы 3 для ECMAScript. Оператор распространения литерала объекта копирует перечисляемые свойства из исходного объекта в целевой объект. В следующем примере показано, как легко скопировать объект после того, как предложение было принято.
let obj = {
one: 1,
two: 2,
}
let newObj = { ...z };
// { one: 1, two: 2 }
Примечание. Это будет действительная только неглубокая копия.
В заключение
Копирование объектов в JavaScript может быть довольно сложной задачей, особенно если вы новичок в JavaScript и не знаете, как работает этот язык. Надеюсь, эта статья помогла вам понять и избежать ошибок, с которыми вы можете столкнуться при дублировании объектов. Если у вас есть какая-либо библиотека или фрагмент кода для получения лучших результатов, поделитесь с сообществом.
Оригинальная ссылка:Scotch.io/put people-talk/co…