如果有一个对象info,想要拷贝里面的内容,有三种方式。
- const info ={
- name: "zzz",
- age: 18,
- friend: {
- name: "www"
- }
- }
info和obj1是相同的引用,修改obj1的name也会影响到info的name。
- //引用赋值
- const obj1 = info
- console.log(obj1)
- //浅拷贝
- const obj2 = {...info}
- obj2.name = "jx"
- console.log(obj2.name) //jx
使用展开运算符,拷贝了一个新的对象obj2,修改里面的name,info不会跟着改变。
- const obj2 = {...info}
- obj2.friend.name = "jx"
- console.log(obj2.friend.name) //jx
- console.log(info.friend.name) //jx
但是如果修改的是对象,info的对象也会跟着发生变化。
浅拷贝可以拷贝原始类型,不能拷贝复杂类型比如对象等。
还有一种浅拷贝的方式。
const obj3 = Object.assign({}, info)
Object.assign方法,将后面的info拷贝到前面的{}对象中,并且返回给obj3。
深拷贝实现方式有两种:使用JSON方法,自己实现
使用JSON方法
const obj4 = JSON.parse(JSON.stringify(info))
这个obj4完全是一个新的对象,修改对象的内容,info的对象内容也不会跟着改变。
但是使用JSON方法有很大的缺陷就是当info里面有其他的值时。
- const info ={
- name: "zzz",
- age: 18,
- friend: {
- name: "www"
- },
- running: function() {},
- [Symbol()]: "abc"
- }
深拷贝下来的内容是没有函数和Symbol的。

我们可以自己手写深拷贝函数。
深拷贝函数的基本类型和对象的实现
首先需要创建一个函数用来判断一个value是否为对象。
注意不能用typeof 来判断value是否为null,因为null用typeof判断,出来的是object。
- function isObject(value){
- const valueType = typeof value
- return (value !== null) && (valueType === "function" || valueType === "function")
- }
- function deepCopy(originValue){
- //如果是原始类型就直接返回
- if(!isObject(originValue)){
- return originValue
- }
- //创建一个新的对象
- const newObj = {}
- //遍历要拷贝的对象,取出它的key
- //递归 重新判断要拷贝的对象里面是否还有对象
- for(const key in originValue){
- newObj[key] = deepCopy(originValue[key])
- }
- //返回新对象
- return newObj
- }
关键的代码是,使用递归不断判断要拷贝的对象里面是否还有对象。
用一个对象来实验一下深拷贝的效果吧。这个对象暂时还没有函数和Symbol类型的判断,后面会做优化的。
- const info ={
- name: "zzz",
- age: 18,
- friend: {
- name: "www",
- address: {
- name: "sz",
- detail: "lky"
- }
- },
- }
-
- const newObj = deepCopy(info)
- console.log(newObj)

深拷贝成功了!
深拷贝函数的数组拷贝
有时候我们需要拷贝数组,在JavaScript中,数组也是一个对象,上面的代码中创建的是一个新的对象,但不一定要创建的是新对象,也有可能是新数组,所以需要先区分对象和数组,再创建对象或者数组。
- function isObject(value) {
- const valueType = typeof value
- return (value !== null) &&(valueType === "object" || valueType ==="function")
- }
- function deepCopy(originValue) {
- //如果是原始类型就直接返回
- if(!isObject(originValue)) {
- return originValue
- }
- //判断是数组还是对象
-
- const newObj = Array.isArray(originValue)? [] :{}
- for(const key in originValue){
- newObj[key] = deepCopy(originValue[key])
- }
- return newObj
- }
关键的一步是通过Array的isArray判断传入的是对象还是数组。
我们创建一个books数组来验证一下是否可以判断对象还是数组来拷贝。
- const books = [
- { name:"book1", price: 1 },
- { name:"book2", price: 2 },
- { name:"book3", price: 3 },
- ]
- const newArray = deepCopy(books)
- console.log(newArray)

拷贝的是一个数组。
深拷贝函数的Set拷贝
有的时候,深拷贝的内容有Set,如果不知道Set是什么的话,可以看这篇JavaScript中Set的使用,Set和数组类似,但是Set的元素是不能重复的,所以它常用于去重。
创建一个set,把它放进info对象里面。
- const set = new Set(["zzz", "wjx", "lky"])
- const info = {
- name: "zzz",
- age: 18,
- friend: {
- name: "www",
- address: {
- name: "sz",
- detail: "lky"
- }
- },
- set: set
- }
- function isObject(value) {
- const valueType = typeof value
- return (value !== null) &&(valueType === "object" || valueType ==="function")
- }
-
- function deepCopy(originValue) {
- //如果是原始类型就直接返回
- if(!isObject(originValue)) {
- return originValue
- }
- //判断是否是set
- if(originValue instanceof Set) {
- const newSet = new Set()
- for(const setItem of originValue) {
- newSet.add(deepCopy(setItem))
- }
- return newSet
- }
- //判断是数组还是对象
- const newObj = Array.isArray(originValue)? [] :{}
- for(const key in originValue){
- newObj[key] = deepCopy(originValue[key])
- }
- return newObj
- }

先判断传入的值是不是Set,创建新的Set,遍历Set,把遍历的元素一个个放到新的Set里面。

可以拷贝Set了。
深拷贝函数的函数返回
函数是不需要深拷贝的,因为函数是用来执行的,如果再拷贝一个函数,会消耗性能。
- if(typeof originValue === "function") {
- return originValue
- }
所以遇到函数的时候直接返回了。