详解js中的浅拷贝与深拷贝

这篇具有很好参考价值的文章主要介绍了详解js中的浅拷贝与深拷贝。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

1、前言

1.1 栈(stack)和堆(heap)

  • 栈(stack):由操作系统自动分配释放 ,存放函数的参数值,局部变量的值等。其操作方式类似于数据结构中的栈;
  • 堆(heap):一般由程序员分配释放, 若程序员不释放,程序结束时可能由OS回收,分配方式倒是类似于链表

1.2 基本数据类型和引用数据类型

1.2.1 概念
  • 基本数据类型:Number、String、Boolean、Null、 Undefined、Symbol(ES6);
  • 引用数据类型:Object、Array、Function、Date、RegExp、Map、Set等。
1.2.2 区别

两者区别:

  1. 内存地址分配:
    1)基本数据类型:将值存储在栈中 ,栈中存放的是对应的值
    2)引用数据类型:将对应的值存储在堆中,栈中存放的是指向堆内存的地址
  2. 赋值变量:
    1)基本数据类型:是生成相同的值,两个对象对应不同的地址
    2)引用数据类型:是将保存对象的内存地址赋值给另一个变量。也就是两个变量指向堆内存中同一个对象
	let a = 10;
	let b = a;  // 赋值操作
	b = 100;
	console.log(a);  // 10

总结:
a是基本类型,存储在栈中;把a赋值给b,虽然两个变量的值相等,但是两个变量保存了两个不同的内存地址。

1.2.3 基本类型赋值方式

详解js中的浅拷贝与深拷贝

1.2.4 引用类型赋值方式
	let obj1 = {}
	let obj2 = obj
	obj2.name = '李四'
	console.log(obj.name)  // 李四

理解:obj1是引用类型,将数据存放在堆内存中,而栈中存放的是内存地址.在obj1赋值给obj2,实际是将obj1的引用地址复制了一份给了obj2,实际上他们共同指向了同一个堆内存对象,所以更改obj2会对obj1产生影响
详解js中的浅拷贝与深拷贝

2、浅拷贝

2.1 概念

会在栈中开辟另一块空间,并将被拷贝对象的栈内存数据完全拷贝到该块空间中,即基本数据类型的值会被完全拷贝,而引用类型的值则是拷贝了“指向堆内存的地址”。

2.2 常见的浅拷贝方法

  • Object.assign()
  • 扩展运算符(…)
  • Array.concat()
  • Array.slice()
2.2.1 Object.assign()

object.assign 是 ES6 中 object 的一个方法,该方法可以用于JS 对象的合并等多个用途,其中一个用途就是可以进行浅拷贝。

object.assign 的语法为:Object.assign(target, …sources)

var obj = { 
	x: 1, 
	y: 2,
	z: { 
		num: 10 
	} 
}
var newObj = {}
Object.assign(newObj, obj)
newObj.y = 3
console.log(obj)  
console.log(newObj) 

运行结果如下:
详解js中的浅拷贝与深拷贝

注意:

  • Object.assign()不会拷贝对象的继承属性;
  • Object.assign()不会拷贝对象的不可枚举的属性;
  • Object.assign()可以拷贝 Symbol 类型的属性。
2.2.2 扩展运算符(…)

扩展运算符的语法为:let cloneObj = { …obj };

/* 对象的拷贝 */
const obj = {
 a: 1,
 b: {
   c: 1
 }
}
const obj2 = {
 ...obj
}
obj.a = 2

console.log(obj)
console.log(obj2); 

obj.b.c = 2

console.log(obj) 
console.log(obj2); 

/* 数组的拷贝 */
let arr = [1, 2, 3];
let newArr = [...arr]; // 跟arr.slice()是一样的效果

运行结果如下:
详解js中的浅拷贝与深拷贝

注意:扩展运算符 和 object.assign 有同样的缺陷,也就是实现的浅拷贝的功能差不多,但是如果属性都是基本类型的值,使用扩展运算符进行浅拷贝会更加方便。

2.2.3 Array.concat()
var obj1 = ["Chinese", { "name": "zs" }, "French"]
var obj2 = obj1.concat()
obj2[1].name = "ls"
obj2[2] = "China"
console.log(obj1, obj2);

运行结果如下:
详解js中的浅拷贝与深拷贝
注意:数组的 concat 方法其实也是浅拷贝,所以连接一个含有引用类型的数组时,需要注意修改原数组中的元素的属性,因为它会影响拷贝之后连接的数组。不过 concat 只能用于数组的浅拷贝,使用场景比较局限。

2.2.4 Array.slice()

slice 的语法为:arr.slice(begin, end);

var arr = [2, 4, 6, { y: 10 }]
var newArr = arr.slice()
newArr[0] = 10
newArr[3].x = 20
newArr[3].y = 30
console.log(arr)
console.log(newArr)

运行结果如下:
详解js中的浅拷贝与深拷贝
注意:slice 方法也比较有局限性,因为它仅仅针对数组类型。slice 方法会返回一个新的数组对象,这一对象由该方法的前两个参数来决定原数组截取的开始和结束位置,是不会影响和改变原始数组的。但是,数组元素是引用类型的话,也会影响到原始数组。

3、深拷贝

3.1 概念

深拷贝是拷贝多层,每一级别的数据都会拷贝出来。

3.2 常见的深拷贝方法

  • JSON.parse(JSON.stringify(obj))
  • 递归方法
  • 函数库 lodash
3.2.1 JSON.parse(JSON.stringify(obj))

原理:用 JSON.stringify 将对象转成 JSON 字符串,再用 JSON.parse()
把字符串解析成对象。一去一来,新的对象就产生了,而且对象会开辟新的栈,实现深拷贝。

let a = {name: '张三', age: 19, like: ['打篮球', '唱歌', '跳舞']}
let b = JSON.parse(JSON.stringify(a))
a.name = '李四'
a.like[0] = '睡觉'
console.log(a)  
console.log(b)  

运行结果如下:
详解js中的浅拷贝与深拷贝
但是,JSON.stringify并不是那么完美的,它也有局限性。

  • 拷贝的对象的值中如果有函数、undefined、symbol 这几种类型,经过 JSON.stringify 序列化之后的字符串中这个键值对会消失;
  • 拷贝 Date 引用类型会变成字符串;
  • 无法拷贝不可枚举的属性;
  • 无法拷贝对象的原型链;
  • 拷贝 RegExp 引用类型会变成空对象;
  • 对象中含有 NaN、Infinity 以及 -Infinity,JSON 序列化的结果会变成 null;
  • 无法拷贝对象的循环应用,即对象成环 (obj[key] = obj)。
let obj = {
	 func: function () { alert(1) },
	 obj: { a: 1 },
	 arr: [1, 2, 3],
	 und: undefined,
	 reg: /123/,
	 date: new Date(0),
	 NaN: NaN,
	 infinity: Infinity,
	 sym: Symbol('1')
}

Object.defineProperty(obj, 'innumerable', {
	enumerable: false,
	value: 'innumerable'
});

console.log('obj', obj); // { NaN: NaN , arr: (3) [1, 2, 3] ,date: Thu Jan 01 1970 08:00:00 GMT+0800 (中国标准时间) {}, func: ƒ(), infinity: Infinity , obj: { a: 1 } , reg: /123/, sym: Symbol(1), und: undefined, innumerable: "innumerable" }

const str = JSON.stringify(obj);

const obj1 = JSON.parse(str);

console.log('obj1', obj1); // { NaN: null, arr: (3) [1, 2, 3], date: "1970-01-01T00:00:00.000Z", infinity: null, obj: {a: 1}, reg: {} }
3.2.2 递归
 function deepCopyTwo(obj) {
    let objClone = Array.isArray(obj) ? [] : {};
     if (obj && typeof obj == 'object') {
         for (const key in obj) {
             //判断obj子元素是否为对象,如果是,递归复制
             if (obj[key] && typeof obj[key] === "object") {
                 objClone[key] = deepCopyTwo(obj[key]);
             } else {
                 //如果不是,简单复制
                 objClone[key] = obj[key];
             }
         }
     }	
     return objClone;
 }
3.2.2 函数库lodash

lodash是一个著名的javascript原生库,不需要引入其他第三方依赖。是一个意在提高开发者效率,提高JS原生方法性能的JS库。简单的说就是,很多方法lodash已经帮你写好了,直接调用就行,不用自己费尽心思去写了,而且可以统一方法的一致性。Lodash使用了一个简单的_ 符号,就像Jquery的 $ 一样,十分简洁。lodash函数库提供 _.cloneDeep用来做深拷贝。文章来源地址https://www.toymoban.com/news/detail-474252.html

	let _ = require('lodash');
	let obj = {
	    a:1,
	    b:{f:{g:1}},
	    c:[1,2,3]
	};
	let newObj = _cloneDeep(obj);
	console.log(obj.b.f === newObj.b.f); //true

4、总结

  • 浅拷贝就是只拷贝基础数据类型的数据,并且数据只有单一的一层,而深拷贝用于拷贝具有复杂的数据类型的数据

5、应用场景

  • 浅拷贝主要用于你需要拷贝的对象的数据结构只有基础数据类型,并且你不想改变原数据类型或者需要对比操作前后的数据。
  • 深拷贝主要用于你想操作该数据,但是又不想影响到原数据的时候,就可以进行深拷贝。

到了这里,关于详解js中的浅拷贝与深拷贝的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • [开发语言][c++][python]:C++与Python中的赋值、浅拷贝与深拷贝

    写在前面 :Python和C++中的赋值与深浅拷贝,由于其各自语言特性的问题,在概念和实现上稍微有点差异,本文将这C++和Python中的拷贝与赋值放到一起,希望通过对比学习两语言实现上的异同点,加深对概念的理解。 C++中所谓的 浅拷贝 就是由(系统默认的) 拷贝构造函数对

    2024年02月02日
    浏览(57)
  • Python中浅拷贝与深拷贝

    在本文中,将介绍如何在Python 3中复制或“克隆”对象,以及所涉及的一些注意事项。 注:本教程是用Python 3编写的,但是在复制对象时,Python 2和3并没有什么区别。当有不同时,会在文中指出。 让我们首先看看如何复制Python的内置集合。Python内置的集合是可变的,如列表、

    2024年02月02日
    浏览(39)
  • python-浅拷贝(copy)与深拷贝(deepcopy)

    目录 一、前言: 二、深拷贝与浅拷贝的异同:         1.相同点:                2.不同点:         3.形象说明:         注意: 三、浅拷贝:  3.1.1浅拷贝示意图: 3.1.2示意图说明         1.对象与子对象(元素)的关系:         2.对象:         3.元

    2024年02月11日
    浏览(36)
  • vue~对象的浅拷贝

    当我们想要复制一段数据的时候吗,我们就会用到拷贝;拷贝数据又分为了浅拷贝和深拷贝,浅拷贝指复制对象或数组的顶层结构,如果对象或数组中有引用类型的属性值,复制的是引用(地址)而非值;而深拷贝则是递归复制完整的对象或数组,包括嵌套的子对象或子数组

    2024年02月09日
    浏览(32)
  • 理解C#中对象的浅拷贝和深拷贝

    本文章主要介绍C#中对象的拷贝,其中包括浅拷贝和深拷贝,以及浅拷贝和深拷贝的实现方式,不同的实现方式之间的性能对比。   浅拷贝是指将对象中的数值类型的字段拷贝到新的对象中,而对象中的引用型字段则指复制它的一个引用到目标对象。如果改变目标对象中引用

    2024年02月08日
    浏览(43)
  • 5个常见的前端手写功能:浅拷贝与深拷贝、函数柯里化、数组扁平化、数组去重、手写类型判断函数

    浅拷贝 浅拷贝是创建一个新对象,这个对象有着原始对象属性值的一份精确拷贝。如果属性是基本类型,拷贝的就是基本类型的值,如果属性是引用类型,拷贝的就是内存地址,所以如果其中一个对象改变了这个地址,就会影响到另一个对象。 测试结果: 深拷贝 深拷贝是将

    2024年04月26日
    浏览(40)
  • 前端 js 栈内存和堆内存 基本数据类型和复杂数据类型的区别?

    先了解一下JavaScript 数据类型有哪些? javaScript 中有8种基本的数据类型:7种为基本数据类型,而Object 为复杂数据类型 基本数据类型(原始数据类型): number 用于任何类型的数字 bigint 用于任意长度的整数。 string 用于字符串 boolean :布尔值 用于 true 和 false。 null 用于未知的

    2024年02月11日
    浏览(47)
  • js深拷贝、浅拷贝

    1.浅拷贝 这个很简单,浅拷贝拷贝的就是地址,其中一个修改另一个也会改变。 浅拷贝拷贝的是地址,只会拷贝最外层的对象(即只会拷贝一层) 拷贝对象:Object.assign(os,obj) || const os={…obj} 拷贝数组:Array.prototype.concat() || […arr] 存在问题:对象内还有对象的时候进修改内部对象的属

    2024年02月12日
    浏览(31)
  • js中的promise详解

           Promise是异步编程的一种解决方案,可以替代传统的解决方案--回调函数和事件。ES6统一了用法,并原生提供了Promise对象。作为对象,Promise有以下两个特点: (1)对象的状态不受外界影响。 (2)一旦状态改变了就不会再变,也就是说任何时候Promise都只有一种状态。    

    2024年02月02日
    浏览(44)
  • JS中深拷贝浅拷贝的区别

    深浅拷贝在MDN官方中的表述是这样的: 对象的 深拷贝 是指其属性与其拷贝的源对象的属性不共享相同的引用(指向相同的底层值)的副本。因此,当你更改源或副本时,可以确保不会导致其他对象也发生更改;也就是说,你不会无意中对源或副本造成意料之外的更改。这种

    2024年02月06日
    浏览(28)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包