js手写深拷贝数组、对象、函数类型数据

  1. 什么是深拷贝?
    深拷贝就是将拷贝后的对象放在一个新开辟的内存空间,拷贝后的变量和原变量中的任何一个被修改都不会对另一个有影响(多层嵌套的数组、对象如果有任意一层值改动影响拷贝后的值都不是深拷贝)。不会像浅拷贝那样只是简单的对指针地址的拷贝,这样两个变量指向的仍然是同一个存储对象,修改任何一个都会做相同改变,属于公用同一个数据。盗用一张图看下基本类型数据和引用类型数据的存储方式,通过它们存储方式不同来理解。
    在这里插入图片描述

    js中基本数据类型Number、String、Boolean、Null、undefined、symbol是存储在中(上图左);引用类型数据Array、Object、Function是放在中存储,(上图右)。栈和堆都是一种数据结构,也是一种内存空间,栈会自动分配内存空间、自动释放,每次方法执行都会创建自己的栈来存放变量,执行结束就会被销毁回收,栈存放的不同数据类型有着自己固定的大小空间限制;能够自由分配内存空间,大小空间不固定,当堆内没有变量去引用数据的时候会被销毁回收
    注:在创建一个对象时,这个对象地址将被保存到栈中,值将被保存到堆内存。堆内存中的对象不会随方法的结束而销毁。当方法结束后,如果这个对象被另一个引用变量所引用,则这个对象依然不会被销毁,只有当一个对象没有任何引用变量引用它时,系统的垃圾回收机制才会在核实的时候回收它。
    举个例子
    ①引用类型:var obj = {name:‘张三’} 会在栈中存入一个变量名为obj值为内存地址的数据,这个内存地址指向堆中存储{name:‘张三’} 的内存空间的起始地址,也就是说obj本质上是一个指针,像简单赋值操作:var obj2 = obj 这两个变量指向的仍然是堆中同一个内存空间的对象,所以数组、对象这种引用类型不能直接赋值实现深拷贝,但有个例外,就是函数能够直接赋值后面会讲下个人理解,很多技术文章都没有说明在递归深拷贝中为什么能将函数进行直接赋值,它不是引用类型数据吗?
    ②基本类型:var a = 2 会在栈中存入变量名为a,值为2的数据,var b = a,会将a的值拷贝一份放入栈中的新内存空间。基本类型数据赋值操作属于深拷贝。

  2. 实现深拷贝的方式
    ① 可通过JSON.parse(JSON.stringify( )) 实现对多层嵌套的数组或对象深拷贝,多层指形如这种格式:[1,[2,3]]、{0:‘a’,1:{2:‘b’}}
    缺陷:当有 undefined、function、symbol 三种类型的值,无法进行拷贝。数组中则会转为 null ,对象中会过滤掉对应的属性名和值。
    ② lodash.js第三方库: _.cloneDeep( )
    ③ 手写深递归(面试常考)

// 类型判断
function judgeType (data) {
	return Object.prototype.toString.call(data).slice(8, -1)
}
// 递归实现深拷贝
function deepCopy (param) {
	let np = judgeType(param) === 'Array'? []:{}
	for (let i in param) {
		if (['Array','Object'].includes(judgeType(param[i]))) {
			np[i] = deepCopy(param[i])
		} else {
			// 包括函数,也在此直接赋值
			np[i] = param[i]
		}
	}
	return np
}
var obj = {0:'a',1:{2:'b'}}
console.log(deepCopy(obj))
// 打印出: {0:'a',1:{2:'b'}}

递归实现深拷贝思路很简单:就是先创建一个空数组或对象,对值遍历,是基本类型还有值为函数的就直接赋值,不是就再递归调用当前处理函数即不是的时候又创建空数组或对象来存储引用类型的值。

  1. 现在来说说上面留下的坑,为什么函数同为引用类型的值,却能够直接赋值操作呢?
    最开始我也会有这样的疑问就是,既然函数是引用类型,存放于堆中,像 例如:var obj = { fun1: function () {} }; var obj2 = { }; obj2.fun1=obj.fun1; obj.fun1 存的是指针,直接赋值应该也是简单指针赋值,指向也是相同对象啊?但事实并不是如此,若修改obj中的fun1函数:obj.fun1 = function () { console.log(1) } 当前obj.fun1会指向一个新的堆内存空间地址,它的指针地址已改变,也就是说如果修改函数,都会通过new function 创建一个新的函数,会新开辟一个内存空间,原有的函数保留,故上面操作后,obj2.fun1还是 function () {} ,指针地址没变,而 obj.fun1则是修改后的function () { console.log(1) } 。
    可以用对象的这种赋值操作加深理解:var obj3 = {0: ‘a’}; var obj4 = obj3; obj3 = {0: ‘a’,1: ‘b’} 打印obj4会发现还是{0: ‘a’},因为形如{}或[]这种直接赋值相当于会新建一个对象或数组,并不是在原有对象和数组上做修改,同理函数修改要通过function声明此刻就是新建一个函数。故上面深拷贝中函数只能通过直接赋值处理

附上一篇文章加深对堆和栈的理解:
栈和堆相关讲解

  • 1
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值