面试被问道深拷贝和浅拷贝,平常只知道用,没去追究差别的我,只能凭着最近的学习回答:"这两个之间最大的差别应该和数据的引用地址相关。"
等面试结束后,再去查,发现我说的不搭边。
为什么会有两种拷贝?
浅拷贝在某些情况下可能更高效,因为它只是复制了引用,而不需要递归复制所有的属性或元素。 而深拷贝则可以保证原始对象和拷贝对象之间的完全独立性,避免了原始对象的变动对拷贝对象造成的影响。
-
基本数据类型(Primitive Data Types):
- 字符串(String):表示文本数据。
- 数字(Number):表示数字。
- 布尔值(Boolean):表示true或false。
- undefined:表示未定义的值。
- null:表示空值。
- Symbol(ES6新增):表示唯一的标识符。
-
引用数据类型(Reference Data Types):
- 对象(Object):表示复杂的数据结构。
- 数组(Array):表示有序的数据集合。
- 函数(Function):表示可执行的代码块。
- 日期(Date):表示日期和时间。
- 正则表达式(RegExp):表示用于模式匹配的文本字符串。
- 基本数据类型存储在栈内存中,而引用数据类型存储在堆内存中。
- 基本数据类型是按值访问的,每个变量都拥有自己的值,修改一个变量不会影响到其他变量。
- 引用数据类型是按引用访问的,多个变量指向同一个对象,修改其中一个变量会影响到其他变量。
都怎么使用?
浅拷贝使用方法:
- 直接相等 let a=1; let b=a;
- Array.slice();
- Array.from();
- Array.concat()
- Object.assing()
let a = 2;
let b = a;
console.log(b) // 2
const list = [1,2,3]
const list1 = list.slice()
const list2 = Array.from(list)
const list3 = list.concat()
const list4 = Object.assign(list)
const person = {
name: 'Tom',
age: 2,
hobby: ['drawing', 'play basketball']
}
const person1 = person
const person2 = Object.assign(person)
console.log(person1) // {name: 'Tom', age: 2, hobby: Array(2)}
person.hobby.push('play the guitar')
console.log(person1) // // {name: 'Tom', age: 2, hobby: Array(3)}
console.log(person2) // // {name: 'Tom', age: 2, hobby: Array(3)}
console.log(list1) // list = [1,2,3]
深拷贝使用方法
- 使用JSON.parse(JSON.stringify())进行深拷贝,但该方法有一些限制,例如无法拷贝函数、循环引用等。
- 使用递归函数自行实现深拷贝。
- 使用第三方库,如lodash的cloneDeep()方法。
const person = {
name: 'Tom',
age: 2,
hobby: ['drawing'],
play: function () {
console.log('can play the guitar')
}
}
const person1 = JSON.parse(JSON.stringify(person))
person.hobby.push('play basketball')
console.log(person) // {name: 'Tom', age: 2, hobby: Array(2), play: ƒ}
console.log(person1) // {name: 'Tom', age: 2, hobby: Array(1)}
function deepCopy (obj, map = new WeakMap()) {
// 基本数据类型直接返回
if (typeof obj !== 'object' && obj === null) {
return obj
}
// 函数 正则 日期 map, set 执行对应构造,返回新的对象
const constructor = obj.constructor
if (/^(Function|RegExp|Date|Map|Set)$/i.test(constructor.name)) {
return new constructor(obj)
}
// 解决共同引用 循环引用问题
// 用 WeakMap 记录每次复制过的对象, 在递归过程中, 如果遇到已经复制过的对象,
// 则直接使用上次拷贝的对象, 不重新拷贝
if (map.get(obj)) {
return map.get(obj)
}
// 创建新对象
const newObj = Array.isArray(obj) ? [] : {}
map.set(obj, newObj)
// 循环加递归
let keys = Object.keys(obj), key = null, val = null;
for (let i=0; i<keys.length; i++) {
key = keys[i]
val = obj[key]
if (val && typeof val === 'object') {
newObj[key] = deepCopy(val, map)
} else {
newObj[key] = val
}
}
return newObj
}
总结:
- 深拷贝和浅拷贝的用法不同:浅拷贝用于基本数据类型的拷贝,深拷贝用于复杂类型的拷贝;
- 产生的效果不同,浅拷贝,拷贝的是个引用,当源数据变了后,拷贝后的数据也会变化;但深拷贝是另外再new了个值出来,源数据的变化和拷贝后的数据不会变化。