前言
要想了解浅拷贝和深拷贝
-----需先了解【基本类型和引用类型】
-----需先了解【堆和栈】
这里写目录标题
逆推了解一下浅拷贝
-
区分浅拷贝和深拷贝
- 假设B复制了A,当修改A时,看B是否会发生变化,如果B也跟着变了,说明这是浅拷贝,相当于镜子,如果B没变,那就是深拷贝,相当于独立存在
let a=[0,1,2,3,4],
b=a;
console.log(a===b);
a[0]=1;
console.log(a,b);
//发现,明明改变了a,但b也随之改变,而且,如果改变b,a也随之改变
//搜嘎,此乃浅拷贝
-
为什么会这样呢?这就关系数据类型的概念了
-
基本数据类型
-
引用数据类型
//基本数据类型
1:数值(数字):number
2:字符串:string
3:命名未赋值:undefined
4:布尔类型:Boolean
5:空类型:null
-基本类型的值是不可变得
-基本类型的比较是值的比较
-基本类型的变量是存放在栈区的(栈区指内存里的栈内存)当 变量传值时,内存中产生新的副本,即进行克隆
//引用数据类型
引用数据类型(Object类)有常规名值对的无序 对象 {a:1},数组[1,2,3],以及函数等
-键存在栈内存中,值存在于堆内存中,但是栈内存会提供一个引用的地址指向堆内存中的值
-
为什么呢,更加疑惑了,没关系,大致看一下,继续了解一个关键知识。堆和栈
- 栈内存
- 堆内存
栈内存
//栈内存
-栈是一种先进后出的数据结构,栈内存是内存中用于存放临时变量的一片内存块。
堆内存
-堆内存的存储不同于栈,虽然他们都是内存中的一片空间,
-但是堆内存存储变量时没有什么规律可言。它只会用一块足够大的空间来存储变量
- 堆内存的优点:引用类型变量大小不固定,它们分配给堆中,
- 让他们申请空间的时候自己确定大小,这样把它们分开存储能够使得程序运行起来占用的内存最小。
- 缺点:堆内存需要分配空间和地址,还要把地址存到栈中,所以效率低于栈
栈内存内存放基本数据类型时,拷贝相当于,连键和值,栈内存会新开辟一个内存存放
-而栈内存存放堆数据的时候,他是 键 存放在栈内存中,但 值 却在堆内存中,
-但栈内存中会提供一个引用的地址指向堆内存中的值
注意:
两者的内存:栈内存中变量一般在它的当前执行环境结束就会被销毁被垃圾回收制回收,
堆内存中的变量只有在所有对它的引用都结束的时候才会被回收。
这就是为什么浅拷贝会造成数据修改变化的原因啦
所以同样是拷贝,拷贝基本数据的时候是值,拷贝引用数据类型的时候是引用地址
故:当修改数组a[0]=1的时候,
由于a与b指向的是同一个地址,所以自然b也受了影响,这就是所谓的浅拷贝
核心了解深拷贝
说到底,问题的核心就是浅拷贝拷贝的时候没有给开辟新的空间,
深拷贝:是会拷贝所有层级的属性
所以,深拷贝的目的,如何实现对引用数据类型进行拷贝时希望它开辟新的堆空间,而非引用原地址的堆空间
深拷贝的误解
//slice方法,作用是对数组进行截取,有两个参数,indexStart 和 indexEnd,分别表示截取的起始位置和结束位置,indexStart是必选的,indexEnd是可选的。
let a=[1,2,3,4];
let b=a.slice();
a[0]=2;
console.log(a,b);
——看结果好像,是起到效果了,但注意的是slice 方法不是深拷贝了,他只拷贝了一层,如果里面是二维数组,他还是会浅拷贝
方法一,
function deepClone(obj) {
//,需要检测的值为 obj,若是为 Array,则返回 true,否则为 false,这个方法只能检测是否为 Array,
let objClone = Array.isArray(obj) ? [] : {};
if (obj && typeof obj === "object") {
for (key in obj) {
//hasOwnProperty() 方法用来检测一个属性是否是对象的自有属性,如果 propertyName 是自有属性,那么返回 true,否则返回 false
//function F() { //自定义数据类型
//this.name = "自有属性";
//}
//F.prototype.name = "继承属性";
//如果是对象的自有属性
if (obj.hasOwnProperty(key)) {
//判断ojb子元素是否为对象,如果是,递归复制
if (obj[key] && typeof obj[key] === "object") {
objClone[key] = deepClone(obj[key]);
} else {
//如果不是,简单复制,意思就是单一一层,无其他层
//第一遍,复制1,2
//第二边,复制数组第三项的1,5
objClone[key] = obj[key];
}
}
}
}
return objClone;
}
// 测试代码
let a = [0, 0, [0,0], 0],
b = deepClone(a);
a[0] = 2;
a[2][0] = 5;
console.log(a, b);
方法二,借用JSON对象的 parse和stringify
var a = [0, 0, [0,0], 0];
var b = JSON.stringify(a);
b = JSON.parse(b);
a[0] = 2;
a[2][0] = 5;
console.log(a, b)
方法三 借用jQuery的extend方法。(未实践)
jQuery.extend() 函数用于将一个或多个对象的内容合并到目标对象。
语法:$.extend( [deep ], target, object1 [, objectN ] )
deep表示是否深拷贝,为true为深拷贝,为false,则为浅拷贝
target Object 类型 目标对象,其他对象的成员属性将被附加到该对象上。
object1 ObjectN 可选。 Object类型 第一个以及第N个被合并的对象
let a = [1, 2, [1, 5], 4];
let b = $.extend(true, [], a);
a[0] = 2;
a[2][0] = 5;
console.log(a, b);