写点什么

【JavaScript】聊一聊 js 中的浅拷贝与深拷贝与手写实现

  • 2024-07-15
    福建
  • 本文字数:2339 字

    阅读完需:约 8 分钟

前言


什么是深拷贝与浅拷贝?深拷贝与浅拷贝是 js 中处理对象或数据复制操作的两种方式。‌在聊深浅拷贝之前咱得了解一下 js 中的两种数据类型:


基本数据类型(6 种)String、Number、Object、Boolean、null、undefined、symbol(ES6+)

引用数据类型 Object(function、Array、正则表达式等皆是对象)


  • 数据的存储方式是什么?

基本数据: 基本数据类型是存放在栈中的简单数据段,它们是直接按值存放的,所以可以直接按值访问引用类型: 引用类型是存放在堆内存中的对象,保存的在栈内存中的一个指针,保存的是栈内存中对象在堆内存中的引用地址。通过这个引用地址可以快速查找到保存中堆内存中的对象。


1.浅拷贝


1.1 什么是浅拷贝


浅拷贝,指的是创建新的数据,这个数据有着原始数据属性值的一份精确拷贝。如果属性是基本类型,拷贝的就是基本类型的值。如果属性是引用类型,拷贝的就是内存地址即浅拷贝是拷贝一层,深层次的引用类型则共享内存地址。


  • 下面用一张图来解释一下浅拷贝



1.2 浅拷贝实现方法


1.2.1 assign

var obj = {	age: 18,	person: {		name1: 'fx',		name2: 'xka'	},	list:['hhh','666'],	love: function () {		console.log('嘿嘿')	}}var newObj = Object.assign({}, obj);//因为是浅拷贝,所以只拷贝了基本类型的,引用类型还是共享内存地址的,即改变obj的应用类型的内容,newObj里面的引用类型的值也随之改变obj.person.name1='xxx'obj.list[0]='xxx'console.log(newObj.person.name1) //xxx
复制代码


1.2.2 slice

const fxArr = ["One", {	name: "Two",	age: 20}, "Three"]const fxArrs = fxArr.slice(0,)fxArr[1].name = "four";console.log(fxArrs[1].name) //four
复制代码


1.2.3 concat

const fxArr = ["One", {	name: "Two",	age: 20}, "Three"]const fxArrs = fxArr.concat()fxArr[1].name = "four";console.log(fxArrs[1].name) //four
复制代码


1.2.4 拓展运算符

const fxArr = ["One", {	name: "Two",	age: 20}, "Three"]const fxArrs = [...fxArr]fxArr[1].name = "four";console.log(fxArrs[1].name) //four
复制代码


2.深拷贝


2.1 什么是深拷贝


深拷贝开辟一个新的栈,两个对象属完成相同,但是对应两个不同的地址,修改一个对象的属性,不会改变另一个对象的属性


  • 下面用一张图来解释一下深拷贝



2.2 浅拷贝实现方法


2.2.1 JSON.parse(常用)

var obj = {	age: 18,	person: {		name1: 'fx',		name2: 'xka'	},	list:['hhh','666'],	love: function () {		console.log('嘿嘿')	}} const obj2=JSON.parse(JSON.stringify(obj));obj.person.name1='6666'console.log(obj2.person.name1) //fx
复制代码


  • 我常用的基本就是 JSON.parse 了,然而其他的,之前听过的 lodash 的 cloneDeep,jq 的 extend 我都没使用过。

  • 但是适用 JSON.parse 会有一个缺点,就是处理的数据里面有 undefined、function、symbol 会被忽略,但是这也是一个优点,可以利用其特性将 undefined 等数据排除,拿到干净的数据。还有一个缺点就是在处理的数据比较大的话,还有性能问题。


3.手写实现深浅拷贝


3.1 浅拷贝

function clone(object){	const newObj={}	for(let proto in object){		if(object.hasOwnProperty(proto)){			newObj[proto]= object[proto]		}	}	return newObj}
复制代码


var obj = {	age: 18,	person: {		name1: 'fx',		name2: 'xka'	},	list:['hhh','666'],	love: function () {		console.log('嘿嘿')	}} const obj1=clone(obj)console.log(obj)console.log(obj1)
复制代码


3.2 深拷贝

// 手写深拷贝function deepClone(obj, hash = new WeakMap()) {	// 数据过滤	if (obj === null) return obj; // 如果是null或者undefined我就不进行拷贝操作 	if (obj instanceof Date) return new Date(obj);// 如果传入的对象是日期对象,使用 new Date() 创建一个新的日期对象并返回	if (obj instanceof RegExp) return new RegExp(obj);// 如果传入的对象是正则表达式对象,使用 new RegExp() 创建一个新的正则表达式对象并返回	// 如果传入的对象不是普通对象(即不是对象类型)或普通的值 如果是函数的话就不需要深拷贝	// 因为拷贝的只需要考虑是否为对象,所以只需要判断obj是否为对象类型即可,因为null或者undefined在上面已经先过滤掉了,此时就只剩下基本数据类型和函数了	if (typeof obj !== "object") return obj;	// 来到此处的话就只剩下对象了,就要进行深拷贝	if (hash.get(obj)) return hash.get(obj);	// 深拷贝	// 创建一个新对象,这个新对象和obj对象类型相同	// 找到的是所属类原型上的constructor属性,而原型上的constructor指向的是当前类本身	let cloneObj = new obj.constructor();	hash.set(obj, cloneObj);	for (let key in obj) {		if (obj.hasOwnProperty(key)) {			// 实现一个递归拷贝			cloneObj[key] = deepClone(obj[key], hash);		}	}	return cloneObj;}
复制代码


var obj = {	age: 18,	person: {		name1: 'fx',		name2: 'xka'	},	list:['hhh','666'],	love: function () {		console.log('嘿嘿')	}}  const obj2 = deepClone(obj) // 深拷贝const obj3 = Object.assign({}, obj) // 浅拷贝const obj4 = clone(obj) // 浅拷贝obj.person.name1 = 'hhh';//因为是深拷贝,obj2中的引用类型新开辟了一个内存地址,所以obj的person改变obj2不受影响console.log(obj2.person.name1) //fx//因为是浅拷贝,obj3、obj4中的引用类型与obj中的引用类型共享内存地址,所以obj的person改变obj3、obj4皆受影响console.log(obj3.person.name1) //hhhconsole.log(obj4.person.name1) //hhh
复制代码


文章转载自:我恨bug

原文链接:https://www.cnblogs.com/nothavebug/p/18300473

体验地址:http://www.jnpfsoft.com/?from=infoq

用户头像

还未添加个人签名 2023-06-19 加入

还未添加个人简介

评论

发布
暂无评论
【JavaScript】聊一聊js中的浅拷贝与深拷贝与手写实现_JavaScript_快乐非自愿限量之名_InfoQ写作社区