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

внешний интерфейс JSON JavaScript

Примитивные типы и ссылочные типы

В ECMAScript есть два типа типов данных:

  • Основные типы: undefined, null, Boolean, String, Number, Symbol
  • Тип ссылки: объект, массив, дата, функция, регулярное выражение и т. д.

Различные типы хранения:

  • основной тип:базовое значение типаЗанимает фиксированный размер в памяти и хранится встек памятисередина
  • Тип ссылки:значение ссылочного типаобъект, хранящийся вкуча памятив, покастек памятихранитсяидентификатор переменной объектатак же какАдрес хранения объекта в куче памяти

Различные типы репликации:

  • Примитивные типы: копирование значения примитивного типа из одной переменной в другую новую переменную создает копию значения и копирует копию в новую переменную
let foo = 1;
let bar = foo;
console.log(foo === bar); // -> true

// 修改foo变量的值并不会影响bar变量的值
let foo = 233;
console.log(foo); // -> 233
console.log(bar); // -> 1
  • Тип ссылки: скопируйте значение ссылочного типа из одной переменной в другую новую переменную, фактически копия является указателем, и, наконец, обе переменные в конечном итоге указывают на один и тот же объект.
let foo = {
  name: 'leeper',
  age: 20
}
let bar = foo;
console.log(foo === bar); // -> true

// 改变foo变量的值会影响bar变量的值
foo.age = 19;
console.log(foo); // -> {name: 'leeper', age: 19}
console.log(bar); // -> {name: 'leeper', age: 19}

Глубокое копирование и поверхностное копирование

  • Неглубокая копия: просто скопируйте ссылку, операции между собой будут влиять друг на друга.
  • Глубокая копия: перераспределить память в куче, разные адреса, одно и то же значение, не влияют друг на друга

В целом, основные различия между глубоким и поверхностным копированием заключаются в следующем:Копируется ли ссылка или копируется экземпляр

Реализация глубокого и поверхностного копирования

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

мелкая копия

  • Array.prototype.slice()
let a = [1, 2, 3, 4];
let b = a.slice();
console.log(a === b); // -> false

a[0] = 5;
console.log(a); // -> [5, 2, 3, 4]
console.log(b); // -> [1, 2, 3, 4]
  • Array.prototype.concat()
let a = [1, 2, 3, 4];
let b = a.concat();
console.log(a === b); // -> false

a[0] = 5;
console.log(a); // -> [5, 2, 3, 4]
console.log(b); // -> [1, 2, 3, 4]

Кажется, что slice() и concat() массива кажутся глубокими копиями, а затем посмотрите на них, чтобы узнать, являются ли они глубокими или поверхностными копиями:

let a = [[1, 2], 3, 4];
let b = a.slice();
console.log(a === b); // -> false

a[0][0] = 0;
console.log(a); // -> [[0, 2], 3, 4]
console.log(b); // -> [[0, 2], 3, 4]

slice()

Также проверьте concat():

let a = [[1, 2], 3, 4];
let b = a.concat();
console.log(a === b); // -> false

a[0][0] = 0;
console.log(a); // -> [[0, 2], 3, 4]
console.log(b); // -> [[0, 2], 3, 4]

Подводя итог, методы slice и concat массива нене настоящая глубокая копия, элементы первого уровня Array являются глубокими копиями, а методы slice и concat второго уровня Array — ссылками на копии. так,Методы slice и concat массива являются мелкими копиями..

глубокая копия

  • JSON.parse() и JSON.stringify()
  1. JSON.stringify(): сериализовать объект js в строку JSON.
  2. JSON.parse(): десериализовать строку JSON в объект js.
let obj = {
  name: 'leeper',
  age: 20,
  friend: {
    name: 'lee',
    age: 19
  }
};
let copyObj = JSON.parse(JSON.stringify(obj));
obj.name = 'Sandman';
obj.friend.name = 'Jerry';
console.log(obj);
// -> {name: "Sandman", age: 20, friend: {age: 19,name: 'Jerry'}}
console.log(copyObj);
// -> {name: "leeper", age: 20, friend: {age: 19,name: 'lee'}}

deep copy

Подводить итоги,JSON.parse() и JSON.stringify() являются полными глубокими копиями..

  • Практическое глубокое копирование Используйте рекурсию для реализации глубоких копий объектов или массивов. Рекурсивная идея: пройтись по всем значениям ссылочного типа в свойстве, пока оно не станет значением базового типа.
function deepCopy(obj) {
  if (!obj && typeof obj !== 'object') {
    throw new Error('error arguments');
  }
  // const targetObj = obj.constructor === Array ? [] : {};
  const targetObj = Array.isArray(obj) ? [] : {};
  for (let key in obj) {
    
    //只对对象自有属性进行拷贝
    if (obj.hasOwnProperty(key)) {
      if (obj[key] && typeof obj[key] === 'object') {
        targetObj[key] = deepCopy(obj[key]);
      } else {
        targetObj[key] = obj[key];
      }
    }
  }
  return targetObj;
}