Оригинальный адрес:Основной менталитет JavaScript — глубокое и поверхностное копирование
Добро пожаловать звезда.
Если у вас неправильное место, пожалуйста, поправьте меня.
Поверхностная копия и глубокая копия предназначены для ссылочных типов в JS. Поверхностная копия — это просто копирование ссылки на объект.Если скопированный объект изменится, исходный объект также изменится. Только глубокие копии являются истинными копиями объектов.
предисловие
Когда речь идет о глубоком и поверхностном копировании, первое, что необходимо упомянуть, — это тип данных JavaScript, предыдущая статьяОсновной менталитет JavaScript — типы данныхЭто очень ясно, поэтому я не буду говорить больше здесь.
Одна вещь, которую вам нужно знать: типы данных JavaScript делятся на базовые типы данных и справочные типы данных.
Для копии основных типов данных нет разницы между глубокими и мелкими копиями. Глубокие и мелкие копии, которые мы называем, - это все для справочных типов данных.
мелкая копия
Неглубокая копия означает, что копируется только ссылка, а не фактическое значение.
const originArray = [1,2,3,4,5];
const originObj = {a:'a',b:'b',c:[1,2,3],d:{dd:'dd'}};
const cloneArray = originArray;
const cloneObj = originObj;
console.log(cloneArray); // [1,2,3,4,5]
console.log(originObj); // {a:'a',b:'b',c:Array[3],d:{dd:'dd'}}
cloneArray.push(6);
cloneObj.a = {aa:'aa'};
console.log(cloneArray); // [1,2,3,4,5,6]
console.log(originArray); // [1,2,3,4,5,6]
console.log(cloneObj); // {a:{aa:'aa'},b:'b',c:Array[3],d:{dd:'dd'}}
console.log(originArray); // {a:{aa:'aa'},b:'b',c:Array[3],d:{dd:'dd'}}
Приведенный выше код является самым простым для использования=
Оператор присваивания реализует поверхностную копию, и ясно видно, что сcloneArray
а такжеcloneObj
Изменять,originArray
а такжеoriginObj
Он также изменился.
глубокая копия
Глубокая копия — это полная копия цели.В отличие от поверхностной копии, которая копирует только слой ссылок, копируется даже значение.
Пока делается глубокая копия, они никогда не будут взаимодействовать друг с другом и не будут влиять друг на друга.
В настоящее время существует не так много способов реализации глубокого копирования, в основном два:
- использовать
JSON
в объектеparse
а такжеstringify
- Используйте рекурсию для воссоздания объектов и присвоения значений на каждом уровне
Метод JSON.stringify/parse
Давайте рассмотрим эти два метода:
The JSON.stringify() method converts a JavaScript value to a JSON string.
JSON.stringify
это поставитьJavaScript
значение вJSON
нить.
The JSON.parse() method parses a JSON string, constructing the JavaScript value or object described by the string.
JSON.parse
это поставитьJSON
преобразовать строку вJavaScript
ценность или предмет.
Это легко понять, этоJavaScript
ценность иJSON
Преобразование строк.
Может ли он достичь глубокого копирования? Давай попробуем.
const originArray = [1,2,3,4,5];
const cloneArray = JSON.parse(JSON.stringify(originArray));
console.log(cloneArray === originArray); // false
const originObj = {a:'a',b:'b',c:[1,2,3],d:{dd:'dd'}};
const cloneObj = JSON.parse(JSON.stringify(originObj));
console.log(cloneObj === originObj); // false
cloneObj.a = 'aa';
cloneObj.c = [1,1,1];
cloneObj.d.dd = 'doubled';
console.log(cloneObj); // {a:'aa',b:'b',c:[1,1,1],d:{dd:'doubled'}};
console.log(originObj); // {a:'a',b:'b',c:[1,2,3],d:{dd:'dd'}};
Это действительно глубокая копия, что тоже очень удобно. Однако этот метод можно использовать только в некоторых простых случаях. Например, объект, подобный следующему, не применяется:
const originObj = {
name:'axuebin',
sayHello:function(){
console.log('Hello World');
}
}
console.log(originObj); // {name: "axuebin", sayHello: ƒ}
const cloneObj = JSON.parse(JSON.stringify(originObj));
console.log(cloneObj); // {name: "axuebin"}
найти вcloneObj
, отсутствуют атрибуты. . . Почему это?
существуетMDN
Причина была найдена:
If undefined, a function, or a symbol is encountered during conversion it is either omitted (when it is found in an object) or censored to null (when it is found in an array). JSON.stringify can also just return undefined when passing in "pure" values like JSON.stringify(function(){}) or JSON.stringify(undefined).
undefined
,function
,symbol
будут игнорироваться в процессе преобразования. . .
Понятно, это означает, что если объект содержит функцию (что очень часто), вы не можете использовать этот метод для создания глубокой копии.
рекурсивный метод
Идея рекурсии очень проста, то есть данные каждого слоя реализуются один раз创建对象->对象赋值
Операция простая и грубая по коду:
function deepClone(source){
const targetObj = source.constructor === Array ? [] : {}; // 判断复制的目标是数组还是对象
for(let keys in source){ // 遍历目标
if(source.hasOwnProperty(keys)){
if(source[keys] && typeof source[keys] === 'object'){ // 如果值是对象,就递归一下
targetObj[keys] = source[keys].constructor === Array ? [] : {};
targetObj[keys] = deepClone(source[keys]);
}else{ // 如果不是,就直接赋值
targetObj[keys] = source[keys];
}
}
}
return targetObj;
}
Давай попробуем:
const originObj = {a:'a',b:'b',c:[1,2,3],d:{dd:'dd'}};
const cloneObj = deepClone(originObj);
console.log(cloneObj === originObj); // false
cloneObj.a = 'aa';
cloneObj.c = [1,1,1];
cloneObj.d.dd = 'doubled';
console.log(cloneObj); // {a:'aa',b:'b',c:[1,1,1],d:{dd:'doubled'}};
console.log(originObj); // {a:'a',b:'b',c:[1,2,3],d:{dd:'dd'}};
Могу. Затем попробуйте еще раз с функцией:
const originObj = {
name:'axuebin',
sayHello:function(){
console.log('Hello World');
}
}
console.log(originObj); // {name: "axuebin", sayHello: ƒ}
const cloneObj = deepClone(originObj);
console.log(cloneObj); // {name: "axuebin", sayHello: ƒ}
Также может. Сделай это.
Как вы думаете, это конец? ? конечно, нет.
Метод копирования в JavaScript
мы знаем, что вJavaScript
, массив имеет два методаconcat
а такжеslice
Можно скопировать исходный массив.Эти два метода не изменят исходный массив, а вернут измененный новый массив.
В то же время ES6 представилObject.assgn
Методы и...
Оператор распространения также может копировать объекты.
Так это поверхностные или глубокие копии?
concat
The concat() method is used to merge two or more arrays. This method does not change the existing arrays, but instead returns a new array.
Этот метод может объединять два или более массивов, но он не изменяет существующие массивы, а возвращает новый массив.
Глядя на это, это похоже на глубокую копию, давайте попробуем:
const originArray = [1,2,3,4,5];
const cloneArray = originArray.concat();
console.log(cloneArray === originArray); // false
cloneArray.push(6); // [1,2,3,4,5,6]
console.log(originArray); [1,2,3,4,5];
Похоже на глубокую копию.
Рассмотрим вопрос, а что если этот объект многослойный.
const originArray = [1,[1,2,3],{a:1}];
const cloneArray = originArray.concat();
console.log(cloneArray === originArray); // false
cloneArray[1].push(4);
cloneArray[2].a = 2;
console.log(originArray); // [1,[1,2,3,4],{a:2}]
originArray
содержит массивы[1,2,3]
и объект{a:1}
, если мы изменим массивы и объекты напрямую, это не повлияетoriginArray
, но мы модифицируем массив[1,2,3]
или объект{a:1}
, нашелoriginArray
также изменился.
В заключение:concat
Просто сделайте глубокую копию первого уровня массива.
slice
The slice() method returns a shallow copy of a portion of an array into a new array object selected from begin to end (end not included). The original array will not be modified.
В пояснении прямо указано, чтоa shallow copy
сейчас ~
Однако это не так!
const originArray = [1,2,3,4,5];
const cloneArray = originArray.slice();
console.log(cloneArray === originArray); // false
cloneArray.push(6); // [1,2,3,4,5,6]
console.log(originArray); [1,2,3,4,5];
Точно так же давайте попробуем многослойные массивы.
const originArray = [1,[1,2,3],{a:1}];
const cloneArray = originArray.slice();
console.log(cloneArray === originArray); // false
cloneArray[1].push(4);
cloneArray[2].a = 2;
console.log(originArray); // [1,[1,2,3,4],{a:2}]
Конечно, результат иconcat
это то же самое.
В заключение:slice
Просто сделайте глубокую копию первого уровня массива.
Object.assign()
The Object.assign() method is used to copy the values of all enumerable own properties from one or more source objects to a target object. It will return the target object.
Копировать копировать копировать.
Так это поверхностная копия или глубокая копия?
Попробуй сам. .
В заключение:Object.assign()
Значение свойства копируется. Если значение свойства исходного объекта является ссылкой на объект, оно также копирует только это значение ссылки.
... оператор спреда
const originArray = [1,2,3,4,5,[6,7,8]];
const originObj = {a:1,b:{bb:1}};
const cloneArray = [...originArray];
cloneArray[0] = 0;
cloneArray[5].push(9);
console.log(originArray); // [1,2,3,4,5,[6,7,8,9]]
const cloneObj = {...originObj};
cloneObj.a = 2;
cloneObj.b.bb = 2;
console.log(originObj); // {a:1,b:{bb:2}}
В заключение:...
Что реализовано, так это глубокая копия первого слоя объекта. Далее следует только скопированное опорное значение.
мелкая копия первого слоя
Мы знаем, что может возникнуть ситуация, когда делается глубокая копия первого слоя целевого объекта, за которой следует поверхностная копия, которую можно назвать «первым слоем поверхностной копии».
Одну из таких функций мы можем реализовать самостоятельно:
function shallowClone(source) {
const targetObj = source.constructor === Array ? [] : {}; // 判断复制的目标是数组还是对象
for (let keys in source) { // 遍历目标
if (source.hasOwnProperty(keys)) {
targetObj[keys] = source[keys];
}
}
return targetObj;
}
Давайте проверим это:
const originObj = {a:'a',b:'b',c:[1,2,3],d:{dd:'dd'}};
const cloneObj = shallowClone(originObj);
console.log(cloneObj === originObj); // false
cloneObj.a='aa';
cloneObj.c=[1,1,1];
cloneObj.d.dd='surprise';
После вышеуказанной модификацииcloneObj
Излишне говорить, определенно{a:'aa',b:'b',c:[1,1,1],d:{dd:'surprise'}}
да, этоoriginObj
Шерстяная ткань? Мы только что подтвердилиcloneObj === originObj
даfalse
, указывая на то, что эти два адреса ссылки на объект различны, что должно быть модификациейcloneObj
не влияетoriginObj
.
console.log(cloneObj); // {a:'aa',b:'b',c:[1,1,1],d:{dd:'surprise'}}
console.log(originObj); // {a:'a',b:'b',c:[1,2,3],d:{dd:'surprise'}}
What happend?
originObj
оa
,c
не пострадали, ноd
Объект в был изменен. . . Как насчет глубокого копирования? Адрес ссылки не отличается?
Оказалось вот что:
- от
shallowClone
Как видно из кода, мы выполнили только первый слой мишени.深拷贝
, а цель в начале второго слоя — напрямую использовать=
Оператор присваивания делает копию. - Итак, цели после второго слоя просто копируют ссылку, то есть поверхностную копию.
Суммировать
- оператор присваивания
=
Реализация представляет собой неглубокую копию, которая копирует только ссылочное значение объекта; - Все методы копирования, которые поставляются с массивами и объектами в JavaScript, являются «поверхностным копированием первого уровня»;
-
JSON.stringify
Реализация является глубокой копией, но есть требования к целевому объекту; - Если вам нужна настоящая глубокая копия, используйте рекурсию.