网站首页 > 技术文章 正文
在 JavaScript 中,因为对象和数组是引用类型,所以当我们直接将它们赋值给另一个变量时,实际上是将它们的引用地址复制了一份。这样,当我们对其中一个变量进行修改时,另一个变量也会受到影响。因此,为了避免这种情况,我们通常需要使用拷贝方法来复制一个对象或数组的值并创建一个新的副本。本文将来介绍浅拷贝和深拷贝的概念以及它们的应用场景。
浅拷贝
浅拷贝是指将原对象或数组的值复制到一个新的对象或数组中,但是新的对象或数组的属性或元素依然是原对象或数组的引用,这意味着当我们修改其中一个对象或数组时,另一个对象或数组也会受到影响。因此,浅拷贝通常只针对引用类型。下面是常见的浅拷贝方法:
1. Object.create(obj)
Object.create() 方法可以用于创建一个新对象,并将原对象作为新对象的原型。这样,新对象就可以访问原对象的所有属性和方法。
示例代码:
const obj1 = { name: '张三', age: 18 };
const obj2 = Object.create(obj1);
console.log(obj2.name); // 张三
obj1.name = '李四'
console.log(obj2.name); //李四
我们可以看到,obj2具有obj1的属性,且当obj1的属性修改时,obj2的这个属性也变了。
2. Object.assign({}, obj)
Object.assign() 方法是用于将一个或多个源对象的属性复制到目标对象中。它的语法如下:
Object.assign(target, ...sources)
其中,target 是目标对象,sources 是一个或多个源对象。
该方法会遍历源对象的所有可枚举属性,并将其复制到目标对象中。如果目标对象中有相同的属性,则后面的属性会覆盖前面的属性。
需要注意的是,该方法只会复制对象自身的属性,不会复制继承自原型链上的属性。而且,如果源对象中有值为 null 或 undefined 的属性,则该属性不会被复制。
浅拷贝示例代码:
let a = {
name:'A',
like:{n: 'coding'}
}
let b = Object.assign({},a)
a.name = 'B'
a.like.n = 'running'
console.log(b.name); // 输出A
console.log(b.like.n); // 输出running
当a中的原始类型属性被修改时,b中的这个属性是不会跟着修改的,但是引用类型被修改则不然,这是因为原始类型与引用类型的存放位置是不同的,在代码执行的时候,原始类型的值存放在调用栈中,而引用类型的值则会被存放在堆中。
新对象b中从a中复制过来的所有原始类型的属性都是新的属性,在b的执行上下文中具有自己的空间地址,而复制过来的引用类型like的值并不是它真正的值,而是它在堆中的引用地址,所以在上述代码中b.like和a.like仍然是同一个对象,它们在调用栈中的值都指向同一个堆中的地址,所以当a.like中的属性被修改时,b.like的属性也变了。
3. [].concat(arr)
[].concat() 方法可以用于将一个或多个数组合并成一个新数组。
示例代码:
let arr = [1,2,3]
let newArr = [].concat(arr)
arr.push(4)
arr[0] = 10
console.log(newArr);//输出[1,2,3]
这个时候修改原数组arr,新数组newArr是不变的,那是不是说明这是深拷贝呢?不,如果数组中的值为对象呢?
let arr = [1,2,3,{n:10}]
let newArr = [].concat(arr)
arr[3].n = 100
console.log(newArr); //输出[1,2,3,{n:100}]
当对象中的值修改时,新数组中的对象中这个值也变了,原理同上一个方法相似。
4. 数组解构 [...arr]
数组解构是一种将数组或类数组对象中的值提取出来,赋值给变量的方法。它可以让我们更方便地访问数组的元素。
[...arr]使用示例代码:
const arr1 = [1, 2, 3];
const arr2 = [...arr1, 4, 5, 6];
console.log(arr2); // [1, 2, 3, 4, 5, 6]
为什么它也是浅拷贝方法呢?
let arr = [1,2,3,{n:10}]
let newArr = [...arr]
arr[3].n = 100
console.log(newArr);
同上面讲的方法一样,它也是原数组有些值修改后会跟着变。
5. arr.slice()
slice() 方法是 JavaScript 数组对象的一个方法,用于从数组中提取指定区间的元素创建一个新的数组,并不会对原数组产生影响。它接受两个参数:开始索引和结束索引,当参数为空时,则会复制整个原数组 arr。这意味着返回的新数组与原数组具有相同的元素、长度和顺序。
示例代码:
const arr1 = [1, 2, 3];
const arr2 = arr1.slice();
console.log(arr2); // [1, 2, 3]
若原数组中值存在对象,则结果也同上面的方法,原数组中的对象中的属性值改变新数组也跟着变。
深拷贝
深拷贝是指将原对象或数组的值复制到一个新的对象或数组中,并且新的对象或数组的属性或元素完全独立于原对象或数组,即它们不共享引用地址。因此,当我们修改其中一个对象或数组时,另一个对象或数组不会受到任何影响。
常见的深拷贝方法是使用 JSON.parse(JSON.stringify(obj)),它的语法如下:
let newObj = JSON.parse(JSON.stringify(obj));
JSON.stringify() 方法是 JavaScript 的一个内置方法,用于将一个 JavaScript 对象或值转换为 JSON 字符串,而JSON.parse()用于将 JSON 字符串解析为对应的 JavaScript 对象或值。
这个方法可以将一个对象序列化为 JSON 字符串,然后再将 JSON 字符串解析为一个新的对象,因为它曾转化为字符串,所以不会出现像浅拷贝中那种引用类型指向同一地址的情况,从而实现深拷贝。
let obj = {
name: 'A',
age:18,
a:{
n:1
}
}
let obj2 = JSON.parse(JSON.stringify(obj))
obj.a.n = 2
console.log(obj2.a.n); //输出1
但是,这种方法也存在一些缺陷:
- 不能处理 undefined、function 和 Symbol 等特殊数据类型,因为它们在 JSON 中没有对应的表示方式。
- 无法处理循环引用,即当一个对象的属性指向自身或形成循环引用关系时,深拷贝会陷入无限递归。
手写实现深拷贝
function deepCopy(obj){
//不是引用类型就不拷贝
if(!(obj instanceof Object)) return obj
//如果形参obj是数组,就创建数组,如果是对象就创建对象
let objCopy = obj instanceof Array ? [] : {}
for(let key in obj){
if(obj instanceof Object){
objCopy[key] = deepCopy(obj[key])
} else{
if(obj.hasOwnProperty(key)){
objCopy[key] = obj[key]
}
}
}
return objCopy
}
猜你喜欢
- 2024-12-09 使用 React 和 Next.js 实现动态搜索表单
- 2024-12-09 短视频文案提取的简单实现
- 2024-12-09 Python应用短文,如何自制一个简易的网络爬虫
- 2024-12-09 面试官:如何用javascript存储函数?
- 2024-12-09 每个前端人都应该知道的20个TypeScript技巧
你 发表评论:
欢迎- 最近发表
-
- 用AI做微信小程序的完整步骤_如何用ai制作微信表情包
- 自习室预约的微信小程序设计与实现-计算机毕业设计源码+LW文档
- 微信小程序开发入门指南_微信小程序开发入门教程
- 写字机器人好用吗? 组装就花了5个小时 还要学习软件、录入字体
- 白描网页版 - 高效准确且免费的OCR文字识别工具
- 字体图形面板与图标字体使用_字体图标的优势和劣势
- 作为前端工程师必须懂得的33个CSS核心概念
- Flutter程序员开发炫酷的登录页面 字体库运用 路由学习 源码分享
- 2025Q3开源字体盘点:让你的代码和文档'颜值'飙升!
- Agent杂谈:Agent的能力上下限及「Agent构建」核心技术栈调研分享~
- 标签列表
-
- 前端设计模式 (75)
- 前端性能优化 (51)
- 前端模板 (66)
- 前端跨域 (52)
- 前端缓存 (63)
- 前端aes加密 (58)
- 前端脚手架 (56)
- 前端md5加密 (54)
- 前端路由 (61)
- 前端数组 (73)
- 前端js面试题 (50)
- 前端定时器 (59)
- Oracle RAC (76)
- oracle恢复 (77)
- oracle 删除表 (52)
- oracle 用户名 (80)
- oracle 工具 (55)
- oracle 内存 (55)
- oracle 导出表 (62)
- oracle约束 (54)
- oracle 中文 (51)
- oracle链接 (54)
- oracle的函数 (58)
- oracle面试 (55)
- 前端调试 (52)
本文暂时没有评论,来添加一个吧(●'◡'●)