1. Понимание глубокого и поверхностного копирования
javascriptобычно естьпередать по значениюа такжепройти по ссылкеДва способа копирования:
передать по значениюдабазовый тип данных(число, строка, логическое значение, нуль, неопределенный, символ), обычно хранящиеся в области стека в памяти, с высокой скоростью доступа и небольшим объемом памяти;
пройти по ссылкедатип ссылки(объект, массив, функция), обычно хранящиеся в области кучи в памяти, с низкой скоростью доступа и большой емкостью памяти, его указатель ссылки хранится в области стека и указывает на саму ссылку.
Глубокие и мелкие копии относятся к ссылочным типам:
мелкая копия: означает, что два объекта js указывают на один и тот же адрес памяти, и изменение одного повлияет на другой;
глубокая копия: означает, что скопированный новый объект указывает на новый адрес памяти, и изменения двух объектов не влияют друг на друга.
2. Поверхностное копирование
Общие методы поверхностного копирования следующие:
- Простая операция присваивания:
var arr = [1,2,3];
var newarr = arr;
newarr[0] = "one";
console.log(arr); // ["one", 2, 3]
console.log(newarr); // ["one", 2, 3]
console.log(arr==newarr); // true
console.log(arr===newarr); // true
-
Object.assign()Метод представляет собой новую функцию ES6, которая может копировать любое количество перечислимых свойств самого исходного объекта в целевой объект, а затем возвращать целевой объект. Копия — это ссылка на свойства объекта, а не на сам объект, но также может быть реализована глубокая копия:
var obj = { a: {a: "hello"}, b: 33 };
var newObj = Object.assign({}, obj);
newObj.a.a = "hello world";
console.log(obj); // { a: {a: "hello world"}, b: 33 };
console.log(newObj); // { a: {a: "hello world"}, b: 33 };
console.log(obj.a.a==newObj.a.a); // true
console.log(obj.a.a===newObj.a.a); // true
-
$.extend({},obj)Поверхностная копия и глубокая копия реализованы с использованием рекурсивной идеи.Первый тип параметра - логический.Если он ложный, то его нужно опустить и не записывать, это поверхностная копия, а когда он истинный, это глубокая копия :
var obj = { a: {a: "hello"}, b: 33 };
var newObj = $.extend({}, obj);
newObj.a.a = "hello world";
console.log(obj); // { a: {a: "hello world"}, b: 33 };
console.log(newObj); // { a: {a: "hello world"}, b: 33 };
console.log(obj.a.a==newObj.a.a); // true
console.log(obj.a.a===newObj.a.a); // true
Поверхностное копирование – это эффективный метод, который мы часто используем для работы с некоторыми объектами или массивами. Конкретное использование должно использоваться разумно в сочетании с реальными сценариями, и необходимо учитывать некоторые проблемы совместимости, но в большинстве практических сценариев нам нужно использовать больше глубокое копирование, особенно в различныхMVVMВо фреймворке введено управление состоянием, которое больше отражается на потоке данных, есть надежда, что мы не меняем исходный объект, в этом случае особенно важно реализовать глубокую копию.
3. Глубокое копирование
Общие методы простого глубокого копирования следующие (одномерная структура данных):
- Ручная операция назначения:
var obj = { a: 10, b: 20};
var newObj = { a: obj.a, b: obj.b};
newObj.b = 100;
console.log(obj); // { a: 10, b: 20}
console.log(newObj); // { a: 10, b: 100};
console.log(obj == newObj); // false
console.log(obj === newObj); // false
-
Object.assign()Этот метод является новой функцией ES6, он может просто копировать слой свойств в целевой объект, и необходимо учитывать совместимость:
var obj = { a: {a: "hello"}, b: 33 };
var newObj = Object.assign({}, obj);
newObj.b = 100;
console.log(obj); // { a: "hello", b: 33 };
console.log(newObj); // { a: "hello", b: 100 };
console.log(obj==newObj); // false
console.log(obj===newObj); // false
Обычно используются следующие методы сложной глубокой копии (двумерная структура данных и выше):
-
JSON.parse(JSON.stringify(obj))Это самая простая и грубая глубокая копия, которая может обрабатывать все типы данных в формате JSON, но не может выполнять глубокую копию для типов регулярных выражений, типов функций и т. д., и напрямую потеряет соответствующее значение и отбросит объект.constructor. Это также глубокая копия, независимо от исходного конструктора, после глубокой копии он станет объектом. В то же время, если в объекте есть ссылка на цикл, она не может быть корректно обработана:
var obj = { a: {a: "hello"}, b: 33 };
var newObj = JSON.parse(JSON.stringify(obj));
newObj.b = "hello world";
console.log(obj); // { a: "hello", b: 33 };
console.log(newObj); // { a: "hello world", b: 33};
console.log(obj==newObj); // false
console.log(obj===newObj); // false
-
$.extend(true,{},obj)Глубокое копирование может быть достигнуто с использованием рекурсивных идей, требующих, чтобы первый параметр был истинным:
var obj = { a: {a: "hello"}, b: 33 };
var newObj = $.extend(true, {}, obj);
newObj.a.a = "hello world";
console.log(obj); // { a: "hello", b: 33 };
console.log(newObj); // { a: "hello world", b: 33 };
console.log(obj==newObj); // false
console.log(obj===newObj); // false
-
lodash中из_.clone(obj, true)Эквивалентно_.cloneDeep(obj)Два метода, lodash тратит много кода на реализацию большого количества новых стандартных объектов, введенных ES6, и обработка объектов с кольцами тоже очень хороша, поэтому lodash является наиболее дружелюбным по сравнению с другими библиотеками для глубокого копирования:
var obj = { a: {a: "hello"}, b: 33 };
var newObj = _.cloneDeep(obj);
newObj.a.a = "hello world";
console.log(obj); // { a: "hello", b: 33 };
console.log(newObj); // { a: "hello world", b: 33 };
console.log(obj==newObj); // false
console.log(obj===newObj); // false
- Реализуйте простую глубокую копию самостоятельно
deepClone():
function deepClone(obj){
if(typeof obj !== "object") return;
let newObj = obj instanceof Array ? [] : {};
for(let key in obj){
if(obj.hasOwnProperty(key)){
newObj[key] = typeof obj[key] === "object" ? deepClone(obj[key]) : obj[key];
}
}
return newObj;
}
let obj = {a: 11, b: function(){}, c: {d: 22}};
deepClone(obj); // {a: 11, b: f(), c: {d: 22}};
Эти методы чаще всего используются для глубокого копирования, и, конечно, есть и другие библиотеки, такие какdeepCopyИ т. д., кроме того, массивы часто используют контакт и срез для достижения глубокого копирования.Разные методы имеют свои наилучшие применимые среды.Это все еще одно и то же предложение: «Уходить со сцены, чтобы говорить о производительности, это хулиганство», давайте использовать данные для анализа одномерная структура данных и две Сравнение производительности многомерных структур данных при разных методах.
4. Сравнение производительности различных методов глубокого копирования
Сравнение времени глубокой копии одномерной структуры данных:
var obj = [];
for (var i = 0; i < 100; i++) {
obj[i] = Math.random();
}
console.time("assign");
var newObj = Object.assign({}, obj);
console.timeEnd("assign");
console.time("JSON.parse(JSON.stringify())");
var newObj = JSON.parse(JSON.stringify(obj));
console.timeEnd("JSON.parse(JSON.stringify())");
console.time("$.extend");
var newObj = $.extend(true, {}, obj);
console.timeEnd("$.extend");
console.time("Loadsh.cloneDeep");
var newObj = _.cloneDeep(obj);
console.timeEnd("Loadsh.cloneDeep");
Object.assign();
Сравнение времени глубокой копии двумерной структуры данных:
var obj = [];
for (var i = 0; i < 100; i++) {
obj[i] = {};
for (var j = 0; j < 100; j++) {
obj[i][j] = Math.random();
}
}
console.time("JSON.parse(JSON.stringify())");
var newObj = JSON.parse(JSON.stringify(obj));
console.timeEnd("JSON.parse(JSON.stringify())");
console.time("$.extend");
var newObj = $.extend(true, {}, obj);
console.timeEnd("$.extend");
console.time("Loadsh.cloneDeep");
var newObj = _.cloneDeep(obj);
console.timeEnd("Loadsh.cloneDeep");
JSON.parse(JSON.stringify());
5. Резюме
Метод глубокого копирования для одномерных структур данных рекомендуется использовать:Object.assign();
Метод глубокого копирования двумерной структуры данных и выше рекомендуется использовать:JSON.parse(JSON.stringify());
Метод глубокого копирования для особо сложных структур данных рекомендуется использовать:Loadsh.cloneDeep();
Для более интересного контента, пожалуйста, обратите внимание на мой паблик [Небесные награды за усердие Льюис]