[Расширенная фаза 4-1] Подробный анализ различий между назначением, поверхностным копированием и глубоким копированием.

интервью внешний интерфейс

Обновление: Спасибо за вашу поддержку.Недавно официальный сайт блога был выкинут, чтобы каждый мог его систематически читать.В будущем будет больше контента и больше оптимизации.Нажмите здесь, чтобы просмотреть

------ Далее идет текст ------

1. Задание (копия)

Присвоение — это процесс присвоения значения или объекта переменной, который делится на следующие две части.

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

Операция присваивания выполняется для базового типа, и две переменные не влияют друг на друга.

// 木易杨
let a = "muyiy";
let b = a;
console.log(b);
// muyiy

a = "change";
console.log(a);
// change
console.log(b);
// muyiy

Назначить ссылочному типусайтДве переменные указывают на один и тот же объект, и изменение переменной a повлияет на переменную b, даже если будут изменены только данные базового типа в объекте a.

// 木易杨
let a = {
    name: "muyiy",
    book: {
        title: "You Don't Know JS",
        price: "45"
    }
}
let b = a;
console.log(b);
// {
// 	name: "muyiy",
// 	book: {title: "You Don't Know JS", price: "45"}
// } 

a.name = "change";
a.book.price = "55";
console.log(a);
// {
// 	name: "change",
// 	book: {title: "You Don't Know JS", price: "55"}
// } 

console.log(b);
// {
// 	name: "change",
// 	book: {title: "You Don't Know JS", price: "55"}
// } 

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

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

1. Что такое мелкое копирование

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

На картинке вышеSourceObjectисходный объект, содержащий свойства примитивного типаfield1и свойства ссылочного типаrefObj. Данные базового типа после поверхностного копированияfield2иfiled1являются разными свойствами и не влияют друг на друга. но ссылочный типrefObjВсе то же самое, после того как изменение повлияет на другой объект.

Проще говоря, можно понять, что мелкое копирование решает только проблему первого слоя и копирует первый слойбазовое значение типа, и первый слойадрес ссылочного типа.

2. Сценарии использования неглубокого копирования

  • Object.assign()

Object.assign()Метод используется для копирования значений всех перечисляемых свойств из одного или нескольких исходных объектов в целевой объект. Он вернет целевой объект.

Некоторые статьи говорятObject.assign()является глубокой копией, что на самом деле неверно.

// 木易杨
let a = {
    name: "muyiy",
    book: {
        title: "You Don't Know JS",
        price: "45"
    }
}
let b = Object.assign({}, a);
console.log(b);
// {
// 	name: "muyiy",
// 	book: {title: "You Don't Know JS", price: "45"}
// } 

a.name = "change";
a.book.price = "55";
console.log(a);
// {
// 	name: "change",
// 	book: {title: "You Don't Know JS", price: "55"}
// } 

console.log(b);
// {
// 	name: "muyiy",
// 	book: {title: "You Don't Know JS", price: "55"}
// } 

После того, как приведенный выше код изменяет объект a, основные свойства объекта b остаются неизменными. Но при изменении объекта в объекте abookПри меняется и соответствующее положение объекта b.

  • Развернуть синтаксисSpread
// 木易杨
let a = {
    name: "muyiy",
    book: {
        title: "You Don't Know JS",
        price: "45"
    }
}
let b = {...a};
console.log(b);
// {
// 	name: "muyiy",
// 	book: {title: "You Don't Know JS", price: "45"}
// } 

a.name = "change";
a.book.price = "55";
console.log(a);
// {
// 	name: "change",
// 	book: {title: "You Don't Know JS", price: "55"}
// } 

console.log(b);
// {
// 	name: "muyiy",
// 	book: {title: "You Don't Know JS", price: "55"}
// } 

Через код вы можете увидеть фактический эффект иObject.assign()это то же самое.

  • Array.prototype.slice()

slice()метод возвращает новый объект массива, который являетсяbeginиend(без учетаend) исходного массива, определяемогомелкая копия. Исходный массив не будет изменен.

// 木易杨
let a = [0, "1", [2, 3]];
let b = a.slice(1);
console.log(b);
// ["1", [2, 3]]

a[1] = "99";
a[2][0] = 4;
console.log(a);
// [0, "99", [4, 3]]

console.log(b);
//  ["1", [4, 3]]

Видно, что изменениеa[1]послеb[0]Значение не изменилось, но изменилосьa[2][0]После этого соответствующийb[1][0]Значение также меняется. инструкцияslice()Метод является поверхностной копией, и соответствующийconcatИ т. д., обратите особое внимание на сложные структуры массивов в работе.

3. Глубокое копирование

1. Что такое глубокая копия

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

2. Сценарии использования глубокого копирования

JSON.parse(JSON.stringify(object))

// 木易杨
let a = {
    name: "muyiy",
    book: {
        title: "You Don't Know JS",
        price: "45"
    }
}
let b = JSON.parse(JSON.stringify(a));
console.log(b);
// {
// 	name: "muyiy",
// 	book: {title: "You Don't Know JS", price: "45"}
// } 

a.name = "change";
a.book.price = "55";
console.log(a);
// {
// 	name: "change",
// 	book: {title: "You Don't Know JS", price: "55"}
// } 

console.log(b);
// {
// 	name: "muyiy",
// 	book: {title: "You Don't Know JS", price: "45"}
// } 

Полное изменение переменной a не влияет на b , это магия глубокого копирования.

Давайте посмотрим, как работает глубокая копия массива.

// 木易杨
let a = [0, "1", [2, 3]];
let b = JSON.parse(JSON.stringify( a.slice(1) ));
console.log(b);
// ["1", [2, 3]]

a[1] = "99";
a[2][0] = 4;
console.log(a);
// [0, "99", [4, 3]]

console.log(b);
//  ["1", [2, 3]]

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

Однако этот метод имеет следующие проблемы.

1. будет проигнорированundefined

2. будет проигнорированsymbol

3. Не удается сериализовать функцию

4. Объекты, которые не могут разрешать циклические ссылки

5, не может быть обработан правильноnew Date()

6, не может справиться с обычным

  • undefined,symbolТри случая функции и будут игнорироваться напрямую.
// 木易杨
let obj = {
    name: 'muyiy',
    a: undefined,
    b: Symbol('muyiy'),
    c: function() {}
}
console.log(obj);
// {
// 	name: "muyiy", 
// 	a: undefined, 
//  b: Symbol(muyiy), 
//  c: ƒ ()
// }

let b = JSON.parse(JSON.stringify(obj));
console.log(b);
// {name: "muyiy"}
  • В случае циклической ссылки будет сообщено об ошибке.
// 木易杨
let obj = {
    a: 1,
    b: {
        c: 2,
   		d: 3
    }
}
obj.a = obj.b;
obj.b.c = obj.a;

let b = JSON.parse(JSON.stringify(obj));
// Uncaught TypeError: Converting circular structure to JSON
  • new Dateслучае результат преобразования неверен.
// 木易杨
new Date();
// Mon Dec 24 2018 10:59:14 GMT+0800 (China Standard Time)

JSON.stringify(new Date());
// ""2018-12-24T02:59:25.776Z""

JSON.parse(JSON.stringify(new Date()));
// "2018-12-24T02:59:41.523Z"

Решение состоит в том, чтобы преобразовать его в строку или метку времени.

// 木易杨
let date = (new Date()).valueOf();
// 1545620645915

JSON.stringify(date);
// "1545620673267"

JSON.parse(JSON.stringify(date));
// 1545620658688
  • При нормальных обстоятельствах,
// 木易杨
let obj = {
    name: "muyiy",
    a: /'123'/
}
console.log(obj);
// {name: "muyiy", a: /'123'/}

let b = JSON.parse(JSON.stringify(obj));
console.log(b);
// {name: "muyiy", a: {}}

PS: Почему существуют эти проблемы?Вы можете изучить JSON.

В дополнение к методу глубокого копирования, описанному выше, обычно используютсяjQuery.extend()иlodash.cloneDeep(), в следующей статье будет подробно представлена ​​реализация исходного кода, так что следите за обновлениями!

4. Резюме

-- Указывает ли он на тот же объект, что и исходные данные Первый уровень данных — это базовый тип данных. Исходные данные содержат дочерние объекты
назначать да Изменение изменит исходные данные Изменение изменит исходные данные
мелкая копия нет ИзменятьНетизменит исходные данные Изменение изменит исходные данные
глубокая копия нет ИзменятьНетизменит исходные данные ИзменятьНетизменит исходные данные

Ссылаться на

js глубокая копия против поверхностной копии

Глубокая копия Java и мелкая копия

Object.assign() из MDN

Синтаксис расширения MDN

Array.prototype.slice() из MDN

Каталог расширенной серии

  • [Расширенная фаза 1] Стек вызовов
  • [Расширенная фаза 2] Закрытие области
  • [Расширенный этап 3] Этот всесторонний анализ
  • [Расширенная фаза 4] Принцип глубокого и поверхностного копирования
  • [Расширенная фаза 5] Прототип Прототип
  • [Расширенная фаза 6] Функции высшего порядка
  • [Расширенная фаза 7] Механизм событий
  • [Расширенная фаза 8] Принцип цикла событий
  • [Расширенная фаза 9] Принцип обещания
  • [Расширенная фаза 10] Принцип асинхронности/ожидания
  • [Расширенная фаза 11] Принцип защиты от сотрясений/дросселирования
  • [Расширенная фаза 12] Подробное модульное объяснение
  • [Расширенная фаза 13] Трудности ES6
  • [Расширенная фаза 14] Обзор компьютерных сетей
  • [Дополнительная проблема 15] Принцип рендеринга в браузере
  • [Расширенная проблема 16] конфигурация веб-пакета
  • [Продвинутый выпуск 17] Принцип работы веб-пакета
  • [Расширенная фаза 18] Внешний мониторинг
  • [Расширенная фаза 19] Междоменное взаимодействие и безопасность
  • [Расширенная фаза 20] Оптимизация производительности
  • [Расширенная фаза 21] Принцип VirtualDom
  • [Расширенная фаза 22] Алгоритм сравнения
  • [Расширенная фаза 23] Двусторонняя привязка MVVM
  • [Расширенная проблема 24] Принцип Vuex
  • [Расширенная проблема 25] Принцип редукции
  • [Дополнительная проблема 26] Принцип маршрутизации
  • [Дополнительная проблема 27] Анализ исходного кода VueRouter
  • [Дополнительная проблема 28] Анализ исходного кода ReactRouter

общаться

Резюме статей расширенной серии:GitHub.com/100 миллионов акционеров/Нет..., в нем качественные фронтальные данные, думаю хорошо заказать звезду.

Я Му Иян, старший фронтенд-инженер в NetEase, следуйте за мной.Сосредоточьтесь на преодолении сложного фронтенд-собеседования каждую неделю. Далее, позвольте мне познакомить вас с миром продвинутого интерфейса и подбодрить друг друга на пути к продвинутому!