JavaScript基础(5)— 深浅拷贝

1、js中的变量数据类型可分为两种类型:基本类型和引用类型。

基本类型值指的是简单的数据段:number、string、Boolean、null、undefined、symbol

  • 引用类型值指那些可能由多个值构成的对象:Object

2、在将一个值赋值给变量时,解析器必须先确定这个值是基本类型值还是引用类型值。

  • 基本数据类型是按值访问的,因为可以操作保存在变量中的实际的值。
  • 引用类型的值是保存在内存中的对象,与其他的语言不通,js不允许直接访问内存中的位置,也就是说不能直接操作对象的内存空间,在操作对象是,实际上是在操作对象的引用还不是实际的对象。

3、js的变量存储方式——栈(stack)、堆(heap)

  • 栈:自动分配内存空间,系统自动释放,里面存放的是基本类型的值和引用类型的地址
  • 堆:动态分配的内存,大小不定,也不会自动释放,里面存放的是引用类型的值在这里插入图片描述
    4、js值传递与址传递

基本类型和引用类型最大的区别实际就是传值和传址的区别

  • 值传递:基本类型采用的是值传递
var str = 'How are you';
var newStr = str;   //两个变量都是基本类型,值传递
newStr = 10

console.log(str); // How are you
console.log(newStr); // 10
  • 址传递:引用类型采用的是址传递
var data = [1, 2, 3, 4, 5];  //引用类型
var newData = data;
newData[0] = 100;

console.log(data[0]); // 100
console.log(newData[0]); // 100

由于data和newData都是引用类型,采用的是址传递,即data将地址传递给newData,那么data和newData指向同一个地址(引用类型的地址存放在栈内存中),而这个地址都指向了堆内存中引用类型的值,当newData改变了这个值的同时,因为data的地址也指向了这个值,他俩的地址是一个,所以data的值也跟着变化。
就好比data租了一间房子,将房子的地址给了newData,newData通过地址找到了房间,当newData对房间对了改变,对于data来说也是可见的。

如何解决上面的问题呢?就要利用浅拷贝或深拷贝了。

5、浅拷贝和深拷贝

浅拷贝只拷贝一层对象的属性,而深拷贝则递归拷贝了所有层级。

  • 浅拷贝:
function shallowClone(source) {
    var target = {};
    for(var i in source) {
        if (source.hasOwnProperty(i)) {
            target[i] = source[i];
        }
    }
    return target;
}
  • 深拷贝:
function deepClone(source) {
	//定义一个对象,用来确定当前的参数是数据还是对象
    var target = Array.isArray(source) ? [] : {};
    //如果source存在,且类型为对象
    if(source && typeof source === 'object') {
    	for(var i in source) {
        	if (source.hasOwnProperty(i)) {
        		//如果source的子元素是对象,递归操作
            	if (source[i] && typeof source[i] === 'object') {
                	target[i] = deepClone(source[i]); // 注意这里
            	} else {
            		//如果不是,直接赋值
                	target[i] = source[i];
            	}
        	}
    	}
    }
    return target;
}

其他拷贝的方法:
(1)利用JSON的两个方法,JSON.stringify(),JSON.parse()来实现最简洁的深拷贝

var arr = ['str', 1, true, [1, 2], {age: 23}]

var newArr = JSON.parse( JSON.stringify(arr) );
newArr[3][0] = 100;
console.log(arr); // ['str', 1, true, [1, 2], {age: 23}]
console.log(newArr); // ['str', 1, true, [100, 2], {age: 23}]

理解:
我们可以理解为,将原始数据转换为新字符串,再通过新字符串还原为一个新对象,这中改变数据类型的方式,间接的绕过了拷贝对象引用的过程,也就谈不上影响原始数据

限制:
这种方式成立的根本就是保证数据在“中转”时的完整性,而JSON.stringify()将值转换为相应的JSON格式时也有缺陷:

  • undefined、任意的函数以及 symbol 值,在序列化过程中会被忽略(出现在非数组对象的属性值中时)或者被转换成 null(出现在数组中时)。
  • 函数、undefined 被单独转换时,会返回 undefined,
    • 如JSON.stringify(function(){}),将一个 JavaScript 对象或值转换为 JSON 字符串
    • JSON.stringify(undefined),方法用来解析JSON字符串,构造由字符串描述的值或对象
  • 对包含循环引用的对象(对象之间相互引用,形成无限循环)执行此方法,会抛出错误。
  • NaN 和 Infinity 格式的数值及 null 都会被当做 null。
  • 其他类型的对象,包括 Map/Set/WeakMap/WeakSet,仅会序列化可枚举的属性。
    所以当我们拷贝函数、undefined等stringify转换有问题的数据时,就会出错,我们在实际开发中也要结合实际情况使用。

(2)Object.assign()

var obj = {a: 1, b: { c: 2 } }
var newObj = Object.assign({}, obj)

newObj.a = 100;
newObj.b.c = 200;
console.log(obj); // {a: 1, b: { c: 200 } }
console.log(newObj) // {a: 100, b: { c: 200 } }

第一层属性没有改变,但第二层却同步改变了。
因为 Object.assign()拷贝的是(可枚举)属性值,假如源值是一个对象的引用,它仅仅会复制其引用值。

(3)… 展开运算符

const originArray = [1,2,3,4,5,[6,7,8]];
const originObj = {a:1,b:{bb:1}};

const cloneArray = [...originArray];
cloneArray[0] = 0;
cloneArray[5].push(9);
console.log(originArray); // [1,2,3,4,5,[6,7,8,9]]

const cloneObj = {...originObj};
cloneObj.a = 2;
cloneObj.b.bb = 2;
console.log(originObj); // {a:1,b:{bb:2}}

… 实现的是对象第一层的深拷贝。后面的只是拷贝的引用值。

(4)jQuery.extend([deep], target, object1, [objectN]);

第一个参数设置为true,则jQuery返回一个深层次的副本,递归地复制找到的任何对象。

*** 参考链接:
1、JavaScript中的深拷贝和浅拷贝
2、JavaScript专题(五)深浅拷贝
3、JavaScript基础新发 深浅拷贝(浅拷贝和深拷贝)
4、深拷贝的终极探索(90%的人都不知道)

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值