深拷贝与浅拷贝
文章目录
深拷贝与浅拷贝的区别
JavaScript 数据存储(堆栈)
- 栈(stack)为自动分配的内存空间,大小和生存期是确定的,它由系统自动释放,寻址比堆快。栈中主要存放的是基本类型的值以及指向堆中的数组或者对象的地址。;
- 堆(heap)则是动态分配的内存,大小不定也不会自动释放。(因此需要浏览器提供的垃圾回收机制进行内存的回收,否则可能会导致内存泄漏)
- 基本类型 (
Number
,String
,Boolean
,Null
,Undefined
,Symbol
…)
名值存储在栈内存中
简单数据段,数据的大小确定,内存空间大小可以分配,直接按值存放,可以直接访问
let a = 1;
let b = a;
栈内存会开辟出一个内存存放name为b,value为1。此时修改a的值并不影响b的值,不过深拷贝本来也只是针对复杂数据类型———传值
- 引用类型 (
Object
,Function
,Array
… )
名存储在栈内存中,值存储在堆内存中
变量为一个存放在栈内存中的指针,指针指向堆内存中的地址。每个空间大小不一样。
let a = [9,5,"周六"];
let b = a;
栈内存中会存放name为a,value为一个指针堆地址-a
,指向堆内存中的地址存放着 [9,5,"周六"]
,当a=b
时,即将a的value指针堆地址-a
复制给了b。
此时a和b指向同一个引用地址,修改其中一个的属性或值,另一个也会受到影响。——— 传址。
如果在栈内存中为b也开辟出一个内存存放b的指针堆地址-b
,然后指针指向的堆内存中堆地址-b
复制a的属性和值 [9,5,"周六"]
,再去修改b的值或属性也就不会影响到a了。
深拷贝与浅拷贝与赋值
赋值 | 浅拷贝 | 深拷贝 | |
---|---|---|---|
与原数据指向同一对象 | 是 | 否 | 否 |
原数据第一层数据为基本数据类型修改新数据 | 原数据一同改变 | 不会引起原数据改变 | 不会 引起原数据改变 |
原数据中包含子对象修改新数据 | 原数据一同改变 | 原数据一同改变 | 不会 引起原数据改变 |
浅拷贝
浅拷贝呀浅拷贝
创建一个新对象,这个对象有着原始对象属性值的一份精确拷贝。如果属性是基本类型,拷贝的就是基本类型的值,如果属性是引用类型,拷贝的就是内存地址。所以如果其中一个对象改变了这个地址,就会影响到另一个对象。
- 浅拷贝只拷贝了源对象的第一层,即拷贝第一层的基本类型值,以及第一层的引用类型地址。
浅拷贝的实现
for…in 只循环第一层
function simpleCopy(obj) {
var newObj = Array.isArray(obj) ? [] : {};
for (let i in obj) {
newObj[i] = obj[i];
}
return newObj;
}
拓展运算符...
Object.assign()
Object.assign(target,source)
方法用于将源对象(source)的所有可枚举属性复制到目标对象(target)
let a = {
weather: "fine",
date: {
year: 2020,
month: "September",
date: "5th",
time: "17:30",
},
};
let b = Object.assign({}, a);
console.log(b);
// b:{
// weather: "fine"
// date: {year: 2020, month: "September", date: "5th", time: "17-31"}
// }
a.weather = "clear";
a.date.time = "17-31";
console.log(a);
//a:{
// weather: "clear"
// date: {year: 2020, month: "September", date: "5th", time: "17-31"}
//}
console.log(b);
//b:{
// weather: "fine"
// date: {year: 2020, month: "September", date: "5th", time: "17-31"}
//}
const deepClone=(obj)=>{
// 通过Object.getPrototypeOf函数得到obj被克隆函数的原型上的属性
var proto=Object.getPrototypeOf(obj);
return Object.assign({},Object.create(proto),obj);
}
Array.prototype.slice()
Array.prototype.slice(start,end)
(不包含end)
Array.prototype.concat()
Array.from()
jQuery
$.extend(false,{},original)
深拷贝
深拷贝呀深拷贝
深拷贝会对原对象的每一层的值和对象进行拷贝,并拷贝属性指向的动态分配的内存。当对象和它所引用的对象一起拷贝时即发生深拷贝。深拷贝相比于浅拷贝速度较慢并且花销较大。拷贝前后两个对象互不影响。
深拷贝的实现
通过js的内置对象JSON
JSON.parse(JSON.stringify(obj))
- 一些限制or问题
- obj里是RegExp,Error对象 ==> 空对象
- obj里是时间对象 ==> 时间将是字符串的形式,而不是时间对象
- obj里是NaN,Infinity,-Infinity ==> null
- obj里有函数,undefined ==> 丢失
- obj对象有构造函数生成 ==> 丢弃对象的constructor
- 无法处理循环引用的对象
- 无法拷贝obj对象原型链上的属性和方法
递归
function deepClone(source) {
//判断拷贝的要进行深拷贝的是数组还是对象,是数组的话进行数组拷贝,对象的话进行对象拷贝
var objClone = Array.isArray(obj) ? [] : {};
if (obj && typeof obj === "object") {
for (key in obj) {
if (obj.hasOwnProperty(key)) {
if (obj[key] && typeof obj[key] === "object") {
objClone[key] = deepClone(obj[key]);
} else {
objClone[key] = obj[key];
}
}
}
}
return objClone;
}
加上RegExp
和Date
function deepClone(obj) {
if (obj === null) {
return null
}
if(typeof obj !== "object"){return obj}
if (obj.constructor === Date) {
return new Date(obj)
}
if (obj.constructor === RegExp) {
return new RegExp(obj)
}
var newObj = new obj.constructor()
for(var key in obj){
if(obj.hasOwnProperty(key)){
var val = obj[key]
newObj[key] = typeof val === "object"? arguments.callee(val):val
}
}
return newObj
}
循环引用
function deepClone(obj) {
// hash表,记录所有的对象的引用关系
let map = new WeakMap();
function dp(obj) {
let keys = Object.keys(obj);
//如果这个对象已经被记录则直接返回
if (map.get(obj)) {
return map.get(obj);
}
let result = {};
map.set(obj, result);
for (let i = 0, len = keys.length; i < len; i++) {
let key = keys[i];
let temp = obj[key];
if (temp && typeof temp === "object") {
result[key] = dp(temp);
} else {
result[key] = temp;
}
}
return result;
}
return dp(obj);
}
jQuery的$.extend()
$.extend(true,{},original)
zepto 深拷贝参考
// 内部方法:用户合并一个或多个对象到第一个对象
// 参数:
// target 目标对象 对象都合并到target里
// source 合并对象
// deep 是否执行深度合并
function extend(target, source, deep) {
for (key in source)
if (deep && (isPlainObject(source[key]) || isArray(source[key]))) {
// source[key] 是对象,而 target[key] 不是对象, 则 target[key] = {} 初始化一下,否则递归会出错的
if (isPlainObject(source[key]) && !isPlainObject(target[key]))
target[key] = {}
// source[key] 是数组,而 target[key] 不是数组,则 target[key] = [] 初始化一下,否则递归会出错的
if (isArray(source[key]) && !isArray(target[key]))
target[key] = []
// 执行递归
extend(target[key], source[key], deep)
}
// 不满足以上条件,说明 source[key] 是一般的值类型,直接赋值给 target 就是了
else if (source[key] !== undefined) target[key] = source[key]
}
// Copy all but undefined properties from one or more
// objects to the `target` object.
$.extend = function(target){
var deep, args = slice.call(arguments, 1);
//第一个参数为boolean值时,表示是否深度合并
if (typeof target == 'boolean') {
deep = target;
//target取第二个参数
target = args.shift()
}
// 遍历后面的参数,都合并到target上
args.forEach(function(arg){ extend(target, arg, deep) })
return target
}