什么是深拷贝/深复制?什么是浅拷贝/浅复制?本文将针对深浅拷贝这道常见面试题呈现深浅拷贝的理解,以及深浅拷贝的方法归类。将呈现多种深浅拷贝的方法,包括手写递归深拷贝!
什么是浅拷贝:
浅拷贝(Shallow copy)顾名思义,就是浅层次的拷贝,有多浅呢?浅到只能拷贝一层。浅拷贝是指将某个对象拷贝/复制成一个新的对象,使新的对象具有和被拷贝的对象相同的属性、属性值和方法。但是这种拷贝只拷贝第一层,因为无论是对象还是数组,都能在里面再嵌套数组和对象,都能无限嵌套。而对象/数组都是属于引用数据类型,堆中存储的是引用地址,栈中才存储的是对应数据。
以上描述可能晦涩难懂,更简单的描述如下:
浅拷贝只拷贝对象或者数组的第一层数据,基本数据类型拷贝属性和属性值,引用数据类型拷贝引用地址
因为浅拷贝无法拷贝嵌套的数组或者对象,因此深拷贝应运而出,将由深拷贝解决这个问题!
下面是大C老师的回答:
浅拷贝的方法:
对象浅拷贝有两种常用的方法:
扩展运算符(展开运算符)
const originalObj = { name: "John", age: 25 }; const shallowCopyObj = { ...originalObj };
Object.assign()
const originalObj = { name: "John", age: 25 }; const shallowCopyObj = Object.assign({}, originalObj);
数组常用的浅拷贝方法:
扩展运算符(展开运算符)
const originalObj = { name: "John", age: 25 }; const shallowCopyObj = { ...originalObj };//和这对象的的浅拷贝方法一致
Array.from()方法
const originalArray = [1, 2, 3]; const shallowCopyArray = Array.from(originalArray);
Array.prototype.slice()方法
const originalArray = [1, 2, 3]; const shallowCopyArray = originalArray.slice();
Array.concat()方法
const newArray = array1.concat(array2, array3, ..., arrayN);
什么是深拷贝:
深拷贝(Deep copy)简单的说是将对象全部拷贝,如果有嵌套,则每层都拷贝,并非只拷贝地址,而是拷贝完整的数据,使两个对象之间无论嵌套了多少层对象,都不会互相干扰!
深拷贝:
在堆内存中开辟一个空间,创建一个新对象
递归的拷贝原对象的所有属性和方法,拷贝前后两个对象,相互不影响
下面是大C老师的原话:
深拷贝的方法:
一般来说,深拷贝有三种常用的方式:
1、手写递归深拷贝
这个将放到下面去详解,因为代码量比较多!
2、序列化搭配反序列化
这个就是使用JSON.stringify()和JSON.parse()方法,但是这样也会有一定的缺陷
缺陷:
①拷贝对象的属性值,如果是(function/undefined)/ symbol,这个键值对丢失
②如果深拷贝对象的属性值是RegExp,会变成空对象{}
③如果是NaN,Infinity,属性值会变成null
④如果拷贝日期对象,会变成日期字符串
3、第三方lodash插件库的深拷贝
// 引入js <script src="./js/lodash.min.js"></script> const obj = { name: 'zjl', age: 18, hobby:['dance','music'], book:{ book_name:'宗波尘客', price:66 }, test0:function(){}, test1:undefined, test2:Symbol('id'), test3:new RegExp(/abc/, 'i'), test4:NaN, test5:Infinity, test6:new Date() } // 深拷贝 _是lodash的一个对象,里面有一个方法 cloneDeep() const o = _.cloneDeep(obj) console.log(o)
手写递归深拷贝详解:
将用有一定区别的几种手写递归深拷贝的方式用代码呈现给大家,大家根据自己最容易看懂的去理解,然后能手写出来即可!
代码1:
const obj = { name: 'zjl', age: 18, hobby:['dance','music'], book:{ book_name:'Golang', price:66 } } // 递归实现深拷 function deepClone(obj){ // 1. 如果是基本数据类型,直接返回 if (typeof obj !== 'object') return obj // 2. 判断传入的是数组还是对象,在内存中新创建数组/对象 let newObj = Array.isArray(obj)? [] : {} // 给新创建的对象添加属性和值,遍历传入过来的那个对象的属性,一个一个添加到新对象上 return newObj } const res = deepClone() console.log(res)
代码2:
const obj = { name: 'zjl', age: 18, hobby:['dance','music'], book:{ book_name:'Golang', price:66 } } // 深拷贝,在内存中开辟一个空间,创建一个新的对象, ===> 考虑数组和对象两种情况 // 递归实现深拷 function deepClone(obj){ // 1. 如果是基本数据类型,直接返回 if (typeof obj !== 'object') return obj // 2. 判断传入的是数组还是对象,在内存中新创建数组/对象 let newObj = Array.isArray(obj) ? [] : {} // 给新创建的对象添加属性和值,遍历传入过来的那个对象的属性,一个一个添加到新对象上 for(let key in obj){ if (typeof obj[key] === 'object'){ // 当key 取 hobby时, obj[key] 是 ['dance','music'] 把它传入到deepClone再拷贝 newObj[key] = deepClone(obj[key]) } else { newObj[key] = obj[key] } } return newObj } const res = deepClone(obj) console.log(res) res.hobby[0] = 'rapper' console.log(obj)
代码3:
function deepCopy(obj) { if (typeof obj !== "object" || obj === null) { return obj; // 基本数据类型和 null 直接返回 } var clone = Array.isArray(obj) ? [] : {}; for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) { clone[key] = deepCopy(obj[key]); // 递归复制每个属性的值 } } return clone; } // 示例对象 var originalObj = { name: "John", age: 25, hobbies: ["reading", "running"], address: { street: "123 Main St", city: "New York" } }; // 深拷贝对象 var deepCopyObj = deepCopy(originalObj); // 修改深拷贝对象的属性 deepCopyObj.name = "Jane"; deepCopyObj.hobbies.push("painting"); deepCopyObj.address.city = "Los Angeles"; console.log(originalObj.name); // 输出: "John" console.log(originalObj.hobbies); // 输出: ["reading", "running"] console.log(originalObj.address.city); // 输出: "New York"
代码4:
关于深浅拷贝提示:
这是一道常见面试题,一般面试官会询问什么是深浅拷贝,深浅拷贝的区别,以及可能会让你手写深浅拷贝。本文仅包含常用的深浅拷贝方法,如需要更多方法,可咨询大C老师哦!