Примитивные типы и ссылочные типы
В 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]
Также проверьте 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()
- JSON.stringify(): сериализовать объект js в строку JSON.
- 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'}}
Подводить итоги,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;
}