不知深浅的拷贝

引言

当谈到JavaScript编程和数据处理时,拷贝是一个至关重要的主题。在JavaScript中,拷贝涉及到复制和传递数据,以确保程序能够顺利运行并且避免意外的副作用。无论是针对对象、数组还是其他数据结构,深刻理解如何进行有效的拷贝操作都是每个开发者必须具备的技能。让我们一起深入探讨JavaScript中拷贝的重要性以及实现高效拷贝的方法。

拷贝的引入

提到拷贝,我们首先想起的是日常生活中将文件拷贝到另一个文件中,那么我们就得到了两个不同的相同类型的文件。那么在JavaScript中的拷贝又是怎么样的呢?我们先来看一段简单的代码:

js 复制代码
let a = 10
let b = a
a = 20
console.log(b)//10

这段代码的输出结果是b = 10,可能大家不知道这么简单的问题为什么要拿出来讲,那我要给大家讲的是为什么输出b = 10。首先在调用栈的词法环境中声明了两个变量a,b,接下来给a赋值,再将a的值赋给b,再改变a的值,因为给b的是赋值操作,使得b与a并不是在同一个地址,于是当a改变,b仍然不变。

我们再来看一个对象的拷贝:

js 复制代码
let obj = {
    age:18
}
let obj2 = obj
obj.age = 20
console.log(obj2.age);//20

这段代码的输出结果是 obj2.age=20,掘友们思考一下为什么捏?原来是因为对象在词法环境中创建时,会在堆中开辟一个空间用来储存对象的key和value,而我们的对象在栈中只保存堆中的一个地址,当我们给obj2赋值时,是将obj在堆中储存的地址赋给了obj2,于是,当obj.age改变时,obj2.age会随之而变。

浅拷贝

什么是浅拷贝?在上面举的例子中,对象的拷贝就是一个浅拷贝,也就是当我们复制一个新的数组或者对象以后,改变原数组或者对象中的属性或者值,新数组或者对象也随之改变,这种拷贝我们称之为浅拷贝。那我们来举一些浅拷贝的例子:

1.Object.create(x)

js 复制代码
let a = {
    name:'666',
    like:{
        n: 'coding'
        }
}
let b = Object.create(a)//创建一个空对象并且继承a
a.like.n = 'running'
console.log(b.like.n)//running

Object.create(x)方法的作用是创建一个新对象并且让新对象继承x,在上面的代码中,我们创建了对象b并且继承了a,当 a.like.n = 'running' 运行以后,我们发现 b.like.n 同样改变。

2.Object.assign({},a)

js 复制代码
let a = {
    name:'666',
    like:{
        n: 'coding'
        }
}
let c = Object.assign({},a)//将多个对象合并成一个
a.like.n = 'running'
console.log(c);//{ name: '666', like: { n: 'running' } }

Object.assign({},a)方法的作用是将多个对象合并成一个对象,于是我们使用一个空对象和a合并来实现拷贝的功能。我们可以看到,当 a.like.n 改变时,c随之而变。

3.concat

js 复制代码
let arr = [1,2,3,{a:10}]
let newArr = [].concat(arr)//复制一个数组
arr.push(4)
console.log(newArr);//[1,2,3,{a:10}]
arr[3].a = 20
console.log(newArr);//[1,2,3,{a:20}]

concat方法的作用是复制一个新的数组newArr,当我们向原数组中添加一个4时,打印newArr,发现新数组中并没有添加4,那么是否可以认为concat方法创建的不是浅拷贝呢?我们再来看将 arr[3].a 赋为20,结果,newArr中的 arr[3].a 改变,说明concat创建的数组拷贝仍然是浅拷贝。聪明的掘友应该发现了。当我们将存储在词法环境中为地址的属性赋值给另一个对象或数组,那么这就是浅拷贝。

4.slice

js 复制代码
let originalArray = [1, 2, { a: 3 }];
let shallowCopy = originalArray.slice();
originalArray[0] = 'modified';
originalArray[2].a = 4;
console.log(originalArray); // 输出: ["modified", 2, { a: 4 }]
console.log(shallowCopy); // 输出: [1, 2, { a: 4 }]

slice 方法可以创建一个新的数组,其中包含从原始数组中选择的元素。当使用 slice 方法进行浅拷贝时,它会复制原始数组中的元素值,但不会复制对象本身,也就是说如果数组中的元素是对象,那么新数组中的对应元素将指向相同的对象引用。

5.数组解构

js 复制代码
let originalArray = [1, 2, { a: 3 }];
let shallowCopy = [...originalArray];
originalArray[0] = 'modified';
originalArray[2].a = 4;
console.log(originalArray); // 输出: ["modified", 2, { a: 4 }]
console.log(shallowCopy); // 输出: [1, 2, { a: 4 }]

同样的,在数组解构中,如果数组中存在对象,那么当原数组对象中的属性值改变时,新数组也会改变

6.arr.toReversed().reserve()

js 复制代码
let arr = [1,2,3,{a:10}]
let newArr = arr.reverse()//反转数组
arr[0].a = 20
console.log(newArr);//[{a:20},3,2,1]

反转方法也是一样的,当旧数组中存在对象,同样在对象改变时,随之改变

深拷贝

我们说了那么多浅拷贝,那么有没有深拷贝呢?当然有啦掘友们!接下来我们就聊聊深拷贝。 什么是深拷贝呢?聪明的掘友们就会想到当然和浅拷贝相反啦!对,没错!深拷贝就是当我们拷贝一个一个新对象或者数组时,改变原数组或者对象的属性,新数组不变。

在JavaScript中,只存在一种深拷贝:

JSON.parse(JSON.stringify(obj))

js 复制代码
let obj = {
    name:'www',
    age:18,
    like:{
        type: 'coding'
    },
    a:undefined,
    b:null,
    c:function(){},
    d:{
        n:100
    },
    e:Symbol('hello')
}
// obj.c = obj.d
// obj.d.n = obj.c
let newObj = JSON.parse(JSON.stringify(obj))
obj.like.type = 'sleeping'
console.log(newObj);//{name: 'www',age: 18,like: { type: 'coding' },b: null,d: { n: 100}
}

我们可以看到,当我改变obj中对象属性的值时,新数组并没有改变值,这就是深拷贝 但是这个方法有两个缺点:

  • 无法拷贝 undefined,function,Symbol,bigint 这几种类型的数据

    我们可以从上方代码的输出结果来看,undefined,function,Symbol,bigint 这几种类型的数据都未拷贝下来

  • 无法拷贝循环引用的对象

    当我们将 // obj.c = obj.d // obj.d.n = obj.c 两行注释解除后,运行发现报错,说明无法拷贝循环引用的对象

结尾

相信掘友们看完这篇文章功力大有增进!下一篇我将带大家手写浅拷贝和深拷贝!

相关推荐
前端风云志6 分钟前
TypeScript实用类型之Omit
javascript
烛阴18 分钟前
Puppeteer入门指南:掌控浏览器,开启自动化新时代
前端·javascript
全宝1 小时前
🖲️一行代码实现鼠标换肤
前端·css·html
小小小小宇1 小时前
前端模拟一个setTimeout
前端
萌萌哒草头将军2 小时前
🚀🚀🚀 不要只知道 Vite 了,可以看看 Farm ,Rust 编写的快速且一致的打包工具
前端·vue.js·react.js
芝士加2 小时前
Playwright vs MidScene:自动化工具“双雄”谁更适合你?
前端·javascript
Carlos_sam3 小时前
OpenLayers:封装一个自定义罗盘控件
前端·javascript
前端南玖4 小时前
深入Vue3响应式:手写实现reactive与ref
前端·javascript·vue.js
wordbaby4 小时前
React Router 双重加载器机制:服务端 loader 与客户端 clientLoader 完整解析
前端·react.js
tactfulleaner4 小时前
手撕MHA、MLA、MQA、GQA
面试