Глубокое понимание глубокого и поверхностного копирования в JavaScript.

JavaScript

1. Понимание глубокого и поверхностного копирования

javascriptобычно естьпередать по значениюа такжепройти по ссылкеДва способа копирования:

передать по значениюдабазовый тип данных(число, строка, логическое значение, нуль, неопределенный, символ), обычно хранящиеся в области стека в памяти, с высокой скоростью доступа и небольшим объемом памяти;

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

Глубокие и мелкие копии относятся к ссылочным типам:

  мелкая копия: означает, что два объекта js указывают на один и тот же адрес памяти, и изменение одного повлияет на другой;

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

2. Поверхностное копирование

Общие методы поверхностного копирования следующие:

  1. Простая операция присваивания:
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
  1. 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
  1. $.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. Глубокое копирование

Общие методы простого глубокого копирования следующие (одномерная структура данных):

  1. Ручная операция назначения:
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
  1. 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

Обычно используются следующие методы сложной глубокой копии (двумерная структура данных и выше):

  1. 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
  1. $.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
  1. 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
  1. Реализуйте простую глубокую копию самостоятельно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();

Для более интересного контента, пожалуйста, обратите внимание на мой паблик [Небесные награды за усердие Льюис]