关于javascript:关于JS对象的深浅拷贝

12次阅读

共计 2570 个字符,预计需要花费 7 分钟才能阅读完成。

JavaScript 中的数据类型次要分为 根本数据类型 援用数据类型
常见根本数据类型次要有:undefined, null, 布尔值, 字符串和数值;
援用类型次要是对象 (Object)。
要探讨对象的深浅拷贝问题,首先要理解下 JS 中的堆内存和栈内存的。

栈内存 (stack) 和堆内存(heap)

栈内存中变量个别都是已知大小或者范畴有下限的,可看做简略存储,给人整齐划一的感觉。次要用于存储各种根本类型的变量,包含 Boolean、Number、String、Undefined、Null,以及对象变量的指针。
而堆内存贮存的数据大小,往往都是未知的。对象自身是贮存在堆内存中的。

对堆栈内存数据有点理解之后咱们持续想,当初有 obj1 这么一个对象,咱们心愿依据 obj1 拷贝出一个对象 obj2 来,当初咱们有三大类形式:

  1. 间接赋值
  2. 浅拷贝
  3. 深拷贝

赋值

间接将 obj1 赋值给 obj2,其实赋予的值是 obj1 的指针,此时的赋值,只是“赋址”。也就是 obj1 和 obj2 在栈内存中的值都是 obj1 的指针,都指向堆内存中的 obj1。所以当咱们扭转 obj1 或 obj2 的任何一个的数据时,另一个都会扭转(两者其实是同一对象)。

var obj1 = {
    name: "greennn",
    age: 25,
    bestfriend: {
        name: "xiaoming",
        age: 23
    }
};
var obj2  = obj1;
obj2.age = 20;
obj1.bestfriend.name = "xiaohong";
console.log(obj1.age);   // 20
console.log(obj2.bestfriend.name);  //"xiaohong"
console.log(obj1 === obj2);  // true 

浅拷贝

浅拷贝会将对象的第一层属性值独立的拷贝给新对象。
浅拷贝能够通过创立一个新的空对象,for…in 遍历现有对象的属性增加给新对象;代码更简洁的形式是 Object.assign()办法。也能够应用第三方库,如【Underscore】_.clone()等。
咱们先看看第一种形式封装的写法。

function shallowCopy(obj) {let copy = Array.isArray(obj) ? [] : {};
    for (let i in obj) {copy[i] = obj[i];
    }
    return copy;
}
var obj2 = shallowCopy(obj1);
... 

这里咱们用 Object.assign()办法实现浅拷贝,展现后果。
当对象只有一层属性时:

var obj1 = {
    name: "greennn",
    age: 25
};
var obj2  = Object.assign({}, obj1 );
obj2.age = 20;
console.log(obj1.age)   // 25
console.log(obj1 === obj2)  // false 

当初 obj1 和 obj2 看上去就是两个独立的对象了,的确起到了“拷贝”的成果。但浅拷贝的“浅”字通知咱们这里必定有陷阱,接下来咱们看看对象的某个属性是对象时的状况。

var obj1 = {
    name: "greennn",
    age: 25,
    bestfriend: {
        name: "xiaoming",
        age: 23
    }
};
var obj2  = Object.assign({}, obj1 );
obj2.age = 20;
obj1.bestfriend.name = "xiaoding";
console.log(obj1.age)   // 25
console.log(obj2.bestfriend.name); // "xiaoding"
console.log(obj1 === obj2)  // false 

上例就能看进去,浅拷贝只是复制了一层属性,而且当原对象有多层属性时,第一层的某个属性 prop1 的值指向一个对象时,理论记录的是指针,复制的值也是该属性值对象的指针,即新旧两个对象的 prop1 属性记录都是指向同一对象的指针。看上面示意图会更分明一些:

深拷贝

深拷贝是指将每一层的属性都独立拷贝给新对象,实现两个对象的齐全隔离。
深拷贝的实现有以下几个形式:

  1. 通过 JSON 转化
    这个形式代码简洁,但它存在的问题是只能正确处理 Number, String, Boolean, Array, 等扁平对象,即那些可能被 json 间接示意的数据结构。无奈复制对象中的函数,会疏忽对象中的 undefined。
function jsonCopy(obj) {return JSON.parse(JSON.stringify(obj));
}
var obj2 = jsonCopy({a:1}); 
  1. for···in 循环加递归
    只是一段简略的示例,性能上其实和下面的 JSON 一样,在一些非凡状况下会有问题,为了防止踩坑倡议一看而过。
function deepCopy(obj) {let copy = Array.isArray(obj) ? [] : {};
    for (let i in obj) {
        // 遍历时过滤掉原型链上的属性
        if (obj.hasOwnProperty(i)) {
            // 属性是对象时递归解决
            copy[i] = typeof obj[i] === "object" ? deepCopy(obj[i]) : obj[i];
        }
    }
    return copy;
} 
  1. lodash _.cloneDeep()
    本着不要反复造轮子的准则(除非你能造的比人家好),还是倡议间接应用第三方库 lodash 的办法。lodash 提供的复制办法有两个,_.clone()和_.cloneDeep()。其中_.clone(obj, true)等价于_.cloneDeep(obj)。
var _ = require("lodash");
var obj1 = {
    name: "greennn",
    age: 25,
    bestfriend: {
        name: "xiaoming",
        age: 23
    }
};
var obj2 = _.cloneDeep(obj1);
obj2.age = 20;
obj1.bestfriend.name = "xiaoding";
console.log(obj1.age)   // 25
console.log(obj2.bestfriend.name); // "xiaoming"
console.log(obj1 === obj2)  // false 

能够看出,通过深拷贝之后,obj1 和 obj2 曾经齐全独立,互不干涉了。

正文完
 0