предисловие
В работе вы часто будете сталкиваться с ситуацией оперирования массивами и объектами, обязательно будете "бэкапить" исходные массивы и объекты.
Когда вы на самом деле работаете с ним, вы обнаруживаете, что резервная копия также изменилась.В это время вы в замешательстве, почему это, разве это уже не резервное копирование, и как также изменятся резервные массивы и объекты.
Если вы не совсем понимаете принцип копирования, эта статья может вам немного помочь.
тип данных JavaScript
базовый тип данных
string
,number
,null
,undefined
,boolean
,symbol
(Новое в ES6) Значение переменной хранится в памяти стека, и к значению переменной можно получить прямой доступ и изменить его.
Нет копии базового типа данных, поэтому, например, вы не можете изменить значение значения 1.
тип ссылки
Object
Function
RegExp
Math
Date
Значение представляет собой объект, хранящийся в куче памяти.
Переменная, хранящаяся в памяти стека, является указателем на адрес, соответствующий памяти кучи.
При доступе к ссылочному типу указатель адреса объекта должен быть сначала извлечен из стека, а затем требуемые данные должны быть извлечены из кучи памяти.
мелкая копия
Почему объект массива резервной копии также изменяется?Здесь речь идет о «резервной копии», которую вы используете, на самом деле неглубокая копия
Простая эталонная копия
var a = [1,2,3,4];
var b = a;
a[0] = 0;
console.log(a,b);
Вы можете видеть, что массив a напрямую присваиваетсяb
,a
,b
Ссылка на самом деле является адресом объекта, пока значение адреса изменяется,a
,b
Адрес кучи, на который указывает указатель стековой памяти, также изменится.Эта ссылочная копия только добавляет указатель на стековую память переменной, что не имеет большого значения.
concat, часть массива, присвоить копию объекта
тот же пример
var a = [1,2,3,4];
var b = a.concat();
a[0] = 0;
console.log(a,b);
массив в это времяa[0]
значение становится0
,b
Массив остается неизменным, и некоторые студенты спрашивали, не является ли это глубокой копией.
и да и нет,Array.prototype.slice
а такжеArray.prototype.conca
t выглядит как глубокая копия, но на самом деле это поверхностная копия
var a = [1,2,[3,4],{name:'ccy'}];
var b = a.concat();
a[3].name = 'hs';
console.log(a[3],b[3]);
когда массивa
содержит предметы, Array.prototype.slice
а такжеArray.prototype.cancat
Объекты в скопированном массиве по-прежнему используют один и тот же адрес памяти, поэтому это, по сути, поверхностная копия.
Object.assign
Принцип тот же (ибо свойства объекта все базовые типы, его можно рассматривать как глубокую копию)
var a = {age:18,name:'ccy',info:{address:'wuhan',interest:'playCards'}};
var b = Object.assign(a);
a.info.address = 'shenzhen';
console.log(a.info,b.info);
Как сделать глубокую копию объекта, пожалуйста, держите очки.
Напишите функцию глубокого копирования самостоятельно
var clone = function(obj){
var construct = Object.prototype.toString.call(obj).slice(8,-1);
var res;
if(construct === 'Array'){
res = [];
}else if(construct === 'Object'){
res = {}
}
for(var item in obj){
if(typeof obj[item] === 'object'){
res[item] = clone(obj[item]);
}else{
res[item] = obj[item];
}
}
return res;
};
На первый взгляд кажется, что он может решить проблему, заключающуюся в том, что свойство объекта является объектом, и его можно обходить до тех пор, пока свойство не станет базовым типом;
Но подумайте хорошенько, если есть взаимные ссылки между свойствами объекта, будет бесконечный цикл. Вы можете добавить еще одно суждение.Если атрибут объекта ссылается на указатель объекта, он выйдет из текущего цикла.
глубокая копия
Глубокая копия может полностью устранить недостатки мелкой копии, повторно открывая блок адресов, базовое значение типа атрибутов из глубокой копии такое же.
Встроенные объекты глубоко копируют JSON
JSON
Object — это новый тип, представленный в ES5 (поддерживаются браузеры IE8+),JSON
объектparse
метод может бытьJSON
Десериализовать строку вJS
объект,stringify
метод сериализации объектов JS вJSON
String, с помощью этих двух методов также можно получить глубокую копию объекта.
var a = {age:1,name:'ccy',info:{address:'wuhan',interest:'playCards'}};
var b = JSON.parse(JSON.stringify(a));
a.info.address = 'shenzhen';
console.log(a.info,b.info);
JSON
Может обрабатывать общие объекты для глубокого копирования, но не может обрабатывать такие объекты, как функции и регулярки.
Пользовательская функция глубокого копирования
Мы можем снова оптимизировать функцию пользовательского копирования
var clone = function(obj){
function getType(obj){
return Object.prototype.toString.call(obj).slice(8,-1);
}
function getReg(a){
var c = a.lastIndexOf('/');
var reg = a.substring(1,c);
var escMap = {'"': '\\"', '\\': '\\\\', '\b': '\\b', '\f': '\\f', '\n': '\\n', '\r': '\\r', '\t': '\\t', '\w': '\\w', '\s': '\\s', '\d': '\\d'};
for(var i in escMap){
if(reg.indexOf(i)){
reg.replace(i,escMap[i]);
}
}
var attr = a.substring(c+1);
return new RegExp(reg, attr);
}
var construct = getType(obj);
var res;
if(construct === 'Array'){
res = [];
}else if(construct === 'Object'){
res = {}
}
for(var item in obj){
if(obj[item] === obj) continue;//存在引用则跳出当前循环
if(getType(obj[item]) === 'Function'){
res[item] = new Function("return "+obj[item].toString())();
}else if(getType(obj[item]) === 'RegExp'){
res[item] = getReg(obj[item].toString());
}else if(getType(obj[item]) === 'Object'){
res[item] = clone(obj[item]);
}else{
res[item] = obj[item];
}
}
return res;
};
В принципе можно реализовать глубокое копирование функций и обычных объектов, а локально делать только простые тесты.Если есть проблемы, пожалуйста, комментируйте и вовремя указывайте.
Конечно, как библиотека функцийlodash
из_.cloneDeep
,JQuery
из$.extend
Было достигнуто глубокое копирование, и заинтересованные студенты могут самостоятельно просмотреть исходный код.