深浅拷贝你知道多少?

深浅拷贝你知道多少?

原文链接:https://juejin.cn/post/6844903608010407944

一、基本数据类型 & 引用数据类型

ECMAScript数据类型可分为两种:

  • 基本类型:Number、String、Boolean、null、undefined、Symbol;
  • 引用类型:Object、Array、function、Date、RegExp。

不同类型的数据存储方式:

  • 基本类型:基本类型值在内存中占据固定大小,保存在
  • 引用类型:引用类型的值是对象,保存在堆内存中,而栈内存存储的是对象的变量标识符以及对象在堆内存中的存储地址

不同类型的复制方式:

  • 基本数据类型:从一个变量向另一个新变量复制基本类型的值,会创角这个值的一个副本,并将该副本赋值给新变量,修改源数据,并不会产生连锁反应。
let foo = 1;
let bar = foo;
console.log(foo === bar); // -> true

// 修改foo变量的值并不会影响bar变量的值
let foo = 233;
console.log(foo); // -> 233
console.log(bar); // -> 1

  • 引用数据类型:从一个变量向另一个新变量复制引用类型的值,其实复制的是指针(栈中存储对象在堆中的物理地址),最终两个变量都指向同一个对象,修改其中一个,另一个也会变更。
let foo = {
  name: 'leeper',
  age: 20
}
let bar = foo;
console.log(foo === bar); // -> true

// 改变foo变量的值会影响bar变量的值
foo.age = 19;
console.log(foo); // -> {name: 'leeper', age: 19}
console.log(bar); // -> {name: 'leeper', age: 19}

二、深拷贝 & 浅拷贝

  • 浅拷贝:仅仅只是复制了对象存储在栈中的引用地址,彼此之间的操作会相互影响
  • 深拷贝:在堆中重新分配内存,不同的堆中地址,相同的值,互不影响

总的来说,两者主要的区别是:复制的是引用还是实例

三、深浅拷贝的实现

当采用 = 号进行赋值的时候,

3.1 浅拷贝

3.1.1 Array.prototype.slice()Array.prototype.concat()
let a = [[1,2],2,3,4]
let b = a.slice() // let b = a.concat()
a[0][0] = 10
a[1] = 20
console.log('b', b) //  [[10, 2], 2, 3, 4]

// 对于第一层元素是深拷贝,第二层则是浅拷贝
3.1.2 Object.assign(target, ...sources)

注意目标对象自身也会改变,继承属性不可枚举属性是不能拷贝的

let obj = {
  name: 'shengjingyin',
  cc: {
    name:"juliya"
  },
  sayHi: function () {
    console.log('hello:', this.name)
  }
}
let newObj = Object.assign({},obj)

obj.name = 'juli'
obj.cc.name = 'juli'

console.log('newObj', newObj)	// {name: "shengjingyin", cc: {name: "juli"}, sayHi: ƒ}

针对深拷贝,需要使用其他办法,因为 Object.assign()拷贝的是(可枚举)属性值。

假如源值是一个对象的引用(譬如:上面事例 的 cc 属性),它仅仅会复制其引用值

3.2 深拷贝

3.2.1 JSON.parse(JSON.stringify())
let a = [[1,2],2,3,4]
let b = JSON.parse(JSON.stringify(a))
a[0][0] = 5
a[1] = 20
console.log('b', b)	//	[[1, 2], 2, 3, 4]

// 对于第一层、第二层元素都是深拷贝

let obj = {
  name: 'shengjingyin',
  sayHi: function () {
    console.log('hello:', this.name)
  }
}
let newObj = JSON.parse(JSON.stringify(obj))
console.log('newObj', newObj)
// 无法复制sayHi 函数

缺点:无法序列化对象中的函数,新复制出来的对象不包含原对象的函数

3.2.2 递归
function deepClone(target) {
	if (target == null || typeof target !== 'object') {
		return target
	}
	
	let clone = {}
	if (target instanceof Array) {
		clone = []
	}
	
	for (let key in target) {
		if (target.hasOwnProperty(key)) {
			clone[key] = deepClone(target[key])
		}
	}
	
	return clone
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
JS 中的拷贝分为拷贝浅拷贝两种方式。 1. 浅拷贝浅拷贝只是复制了原始对象的引用,而不是复制对象本身。当对象属性值为基本类型时,修改拷贝后的对象不会影响原始对象,但是当对象属性值为引用类型时,修改拷贝后的对象会影响原始对象。 浅拷贝可以通过以下方式实现: - Object.assign() 方法 ```js let obj1 = {a: 1, b: {c: 2}}; let obj2 = Object.assign({}, obj1); ``` - 扩展运算符 ```js let obj1 = {a: 1, b: {c: 2}}; let obj2 = {...obj1}; ``` - 数组的 slice() 和 concat() 方法 ```js let arr1 = [1, 2, {a: 1}]; let arr2 = arr1.slice(); let arr3 = arr1.concat(); ``` 2. 拷贝拷贝会复制对象本身,而不是对象的引用。当对象属性值为基本类型或引用类型时,都会被完全复制,修改拷贝后的对象不会影响原始对象。 拷贝可以通过以下方式实现: - JSON.parse() 和 JSON.stringify() 方法 ```js let obj1 = {a: 1, b: {c: 2}}; let obj2 = JSON.parse(JSON.stringify(obj1)); ``` - 手写递归实现拷贝 ```js function deepClone(obj) { if (typeof obj !== 'object' || obj === null) { return obj; } let result = Array.isArray(obj) ? [] : {}; for (let key in obj) { if (obj.hasOwnProperty(key)) { result[key] = deepClone(obj[key]); } } return result; } ``` 需要注意的是,使用 JSON.parse() 和 JSON.stringify() 方法进行拷贝时,被复制的对象中不能包含函数、undefined、NaN 等类型,否则会出错。手写实现拷贝时,要考虑循环引用的情况,以避免死循环的发生。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值