首页 > 编程语言 >【JavaScript】聊一聊js中的浅拷贝与深拷贝与手写实现

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

时间:2024-07-13 18:29:47浏览次数:23  
标签:console log JavaScript js person obj 拷贝 const

前言

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

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

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

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

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

1.浅拷贝

1.1 什么是浅拷贝

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

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

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 什么是深拷贝

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

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

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会被忽略,且处理的数据比较大的话,还有性能问题。

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) //hhh
console.log(obj4.person.name1) //hhh

上述为个人学习整理内容,水平有限,如有错误之处,望各位园友不吝赐教!如果觉得不错,请点击推荐和关注!谢谢~๑•́₃•̀๑ [鲜花][鲜花][鲜花]

标签:console,log,JavaScript,js,person,obj,拷贝,const
From: https://www.cnblogs.com/nothavebug/p/18300473

相关文章

  • package.json 脚本配置使用环境文件
    脚本使用环境文件"scripts":{"dev":"vite","build:prod":"vitebuild","build:stage":"vitebuild--modestaging","preview":"vitepreview"}dev:运行开发服务器,默认使用.......
  • [JS] generator基本使用
    next方法与yield关键字generator函数可以返回一个迭代器,通过next方法切换generator的状态。generator函数被调用时并不会执行内部的语句,而是返回一个迭代器对象。迭代器对象首次调用next方法,才开始执行generator函数的语句。直到遇到yield语句,内部的执行中断,返回yield关键字右......
  • 解决HBuilder X运行微信小程序模拟器Error: pages.json解析失败
    前言如果已经排查很久了,那这就不是你的问题了,大概率是由于你曾经创建了一个路径,在指定PagePath的时候又指向了这个路径,这个操作本身没有问题。但是,如果你曾经对这个路径修改过了,那编译器就会有问题,来品鉴一下这个错误。16:15:36.772请注意运行模式下,因日志输出、sourcem......
  • nodejs微信支付安全证书下载,亲测有效
    微信支付是目前非常流行的支付方式之一,很多开发者在集成微信支付时需要下载并使用微信支付的安全证书。本文将详细介绍如何在Node.js环境中下载微信支付安全证书,并提供一个亲测有效的示例代码。前置条件在开始之前,请确保你已经具备以下条件:已注册微信支付商户,并获得商户号和AP......
  • js 树形数组转一维数组,或一维数组转树形数组
    树形数组转一维数组要将一棵树结构(包含children属性)拍平为一个一维数组,可以使用递归或迭代的方法。下面是两种常见的实现方式:1.使用递归functionflattenTree(tree){letresult=[];tree.forEach(node=>{result.push(node);if(node.children){......
  • 基于ssm+vue.js+uniapp的汽车养护管理系统附带文章和源代码部署视频讲解等
    文章目录前言详细视频演示具体实现截图技术栈后端框架SSM前端框架Vue持久层框架MyBaits系统测试系统测试目的系统功能测试系统测试结论为什么选择我代码参考数据库参考源码获取前言......
  • 基于springboot+vue.js+uniapp的江西郊医院血库管理系统附带文章源码部署视频讲解等
    文章目录前言详细视频演示具体实现截图技术栈后端框架SpringBoot前端框架Vue持久层框架MyBaits系统测试系统测试目的系统功能测试系统测试结论为什么选择我代码参考数据库参考源码获取前言......
  • 全栈物联网项目:结合 C/C++、Python、Node.js 和 React 开发智能温控系统(附代码示例)
    1.项目概述本文详细介绍了一个基于STM32微控制器和AWSIoT云平台的智能温控器项目。该项目旨在实现远程温度监控和控制,具有以下主要特点:使用STM32F103微控制器作为主控芯片,负责数据采集、处理和控制逻辑采用DHT22数字温湿度传感器,精确采集环境温湿度数据通过ESP8266WiF......
  • Js 前置,后置补零的原生方法与补字符串 padStart及padEnd
    在工作中,遇到了需要将不满八位的一个字符串进行后补0的操作,所以就在网上学习了关于js原生补充字符串的方法,然后用这篇博客记录下来。目录前置补充字符串 String.prototype.padStart()后置补充字符串String.prototype.padEnd()前置补充字符串 String.prototype.padStart......
  • C# Newtonsoft.Json 高级用法
    一、基本用法Json.Net是支持序列化和反序列化DataTable,DataSet,EntityFramework和Entity的。下面分别举例说明序列化和反序列化。//序列化DataTableDataTabledt=newDataTable();dt.Columns.Add("Age",Type.GetType("System.Int32"));dt.Columns.Add("Name",Type.Get......