深浅拷贝

2022-02-25 20:00:54 浏览数 (1)

深浅拷贝

假设B复制了A,当修改A时,看B是否会发生变化,如果B也跟着变了,说明这是浅拷贝,如果B没变,那就是深拷贝

如果是基本数据类型,名字和值都会储存在栈内存中

代码语言:javascript复制
var a = 1;
b = a; // 栈内存会开辟一个新的内存空间,此时b和a都是相互独立的
b = 2;
console.log(a); // 1

如果是引用数据类型,名字存在栈内存中,值存在堆内存中,但是栈内存会提供一个引用的地址指向堆内存中的值

代码语言:javascript复制
var a=[0,1,2,3,4];
var b=a;
console.log(a===b);    //true
a[0]=1;
console.log(a);    // [1,1,2,3,4]
console.log(b);    // [1,1,2,3,4]

浅拷贝

for循环实现

代码语言:javascript复制
function simpleCopy(obj1) {
    var obj2 = Array.isArray(obj1) ? [] : {};
    for (let i in obj1) {
        obj2[i] = obj1[i];
    }
    return obj2;
}

Object.assign()实现

Object.assign() 方法用于将所有可枚举属性的值从一个或多个源对象复制到目标对象。它将返回目标对象

代码语言:javascript复制
const obj3 = {
    a: 1,
    b: { name: '张三', age: 14, hobby: ['唱', '跳', 'rap'] },
    c: ['周杰伦', '华晨宇', '林俊杰'],
    d: function () {
        console.log('ddd');
    }
}

var obj1 = Object.assign({}, obj3);
// 对于Object.assign(), 如果对象的属性值为简单类型(string, number)
// 通过Object.assign({},obj3)得到的新对象为深拷贝
obj1.a = 3;
console.log(obj1)           // {a: 3, b: {…}, c: Array(3), d: ƒ}
console.log(obj3)           // {a: 1, b: {…}, c: Array(3), d: ƒ}
// 如果属性值为对象或其它引用类型,那对于这个对象而言其实是浅拷贝的。
obj1.b.name = '李四'
console.log(obj1.b.name);   //李四
console.log(obj3.b.name);   //李四

直接赋值

代码语言:javascript复制
var a = [0, 1, 2, 3, 4]
var b = a;
console.log(a === b);   // true
a[0] = 1;
console.log(a);         // [1, 1, 2, 3, 4]
console.log(b);         // [1, 1, 2, 3, 4]

深拷贝

用slice实现对数组的深拷贝

代码语言:javascript复制
// 当数组里面的值是基本数据类型,比如String,Number,Boolean时,属于深拷贝
// 当数组里面的值是引用数据类型,比如Object,Array时,属于浅拷贝
var arr1 = ["1", "2", "3", [4, 5]];
var arr2 = arr1.slice(0);
arr2[1] = "9";
console.log(arr1);  // ["1", "2", "3", Array(2)]
console.log(arr2);  // ["1", "2", "3", Array(2)]
arr2[3][0] = 999
console.log(arr1[3][0]);  // 999
console.log(arr2[3][0]);  // 999

用concat实现对数组的深拷贝

代码语言:javascript复制
// 当数组里面的值是基本数据类型,比如String,Number,Boolean时,属于深拷贝
// 当数组里面的值是引用数据类型,比如Object,Array时,属于浅拷贝
var arr1 = ["1", "2", "3", [4, 5]];
var arr2 = arr1.concat();
arr2[1] = "9";
console.log(arr1);  // ["1", "2", "3", Array(2)]
console.log(arr2);  // ["1", "2", "3", Array(2)]
arr2[3][0] = 999
console.log(arr1[3][0]);  // 999
console.log(arr2[3][0]);  // 999

通过JSON对象来实现深拷贝

它只能深拷贝对象和数组,对于其他种类的对象(比如function),会失真

代码语言:javascript复制
function deepClone2(obj) {
    return JSON.parse(JSON.stringify(obj));
}
var json1 = {
    "name": "cheng",
    "age": 18,
    "fun": function (a) {
        console.log(a);
    }
}
var json2 = deepClone2(json1);
json2.age = 20;
console.log(json1); // {name: "cheng", age: 18, fun: ƒ}
console.log(json2); // {name: "cheng", age: 20}

递归

采用递归去拷贝所有层级属性

  1. 判断是不是原始值
  2. 判断是数组还是对象
  3. 新建响应的数组或对象
  4. 递归
代码语言:javascript复制
function deepclone(obj) {
    const newobj = obj instanceof Array ? [] : {}
    for (let i in obj) {
        // 判断自身属性中是否具有指定的属性(避免克隆了原型上的东西)
        if (obj.hasOwnProperty(i)) {
            // 判断是不是原始值
            if (obj[i] instanceof Object == true) {
                // 不是原始值开始递归
                newobj[i] = deepclone(obj[i])
            } else {
                // 是原始值直接赋值
                newobj[i] = obj[i]
            }
        }

    }
    return newobj
}
  • 通过JSON对象来实现深拷贝

它只能深拷贝对象和数组,对于其他种类的对象(比如function),会失真

代码语言:javascript复制
function deepClone2(obj) {
    return JSON.parse(JSON.stringify(obj));
}
代码语言:javascript复制
var json1 = {
    "name": "cheng",
    "age": 18,
    "fun": function (a) {
        console.log(a);
    }
}
var json2 = deepClone2(json1);
json2.age = 20;
console.log(json1); // {name: "cheng", age: 18, fun: ƒ}
console.log(json2); // {name: "cheng", age: 20}
  • 用slice实现对数组的深拷贝
代码语言:javascript复制
// 当数组里面的值是基本数据类型,比如String,Number,Boolean时,属于深拷贝
// 当数组里面的值是引用数据类型,比如Object,Array时,属于浅拷贝
var arr1 = ["1", "2", "3", [4, 5]];
var arr2 = arr1.slice(0);
arr2[1] = "9";
console.log(arr1);  // ["1", "2", "3", Array(2)]
console.log(arr2);  // ["1", "2", "3", Array(2)]
arr2[3][0] = 999
console.log(arr1[3][0]);  // 999
console.log(arr2[3][0]);  // 999
  • 用concat实现对数组的深拷贝
代码语言:javascript复制
// 当数组里面的值是基本数据类型,比如String,Number,Boolean时,属于深拷贝
// 当数组里面的值是引用数据类型,比如Object,Array时,属于浅拷贝
var arr1 = ["1", "2", "3", [4, 5]];
var arr2 = arr1.concat();
arr2[1] = "9";
console.log(arr1);  // ["1", "2", "3", Array(2)]
console.log(arr2);  // ["1", "2", "3", Array(2)]
arr2[3][0] = 999
console.log(arr1[3][0]);  // 999
console.log(arr2[3][0]);  // 999

考虑循环引用

使用数组

这里使用一个数组,保存已经遍历的数据,再每次递归时,先查找当前递归的值在数组里有没有,如果有,则直接返回数组里面的值(引用),然后跳出循环;如果没有,则往数组里插入当前递归的值,然后继续向下执行。

代码语言:javascript复制
var cat = { name: "tom" }
cat.hobby = cat
function deepclone(obj, queueList) {
    // 找数组里面有没有这个值
    function find(arr, obj) {
        var res = null
        for (var i = 0; i < arr.length; i  ) {
            if (arr[i].key == obj.key) {
                res = arr[i]
            }
        }
        return res
    }
    queueList = queueList ? queueList : []
    // 如果在数组找到这个值,就返回这个值,跳出递归
    if (find(queueList, obj)) {
        return find(queueList, obj)
    } else {
        // 找不到就插入值,继续递归
        queueList.push(obj)
    }
    var newobj = obj instanceof Array ? [] : {}
    for (var i in obj) {
        // 判断自身属性中是否具有指定的属性(避免克隆了原型上的东西)
        if (obj.hasOwnProperty(i)) {
            // 判断是不是原始值
            if (obj[i] instanceof Object == true) {
                // 不是原始值开始递归
                newobj[i] = deepclone(obj[i], queueList)
            } else {
                // 是原始值直接赋值
                newobj[i] = obj[i]
            }
        }

    }
    return newobj
}

console.log(deepclone(cat))

使用WeakMap

和数组原理一只,只不过使用了es6的WeakMap

代码语言:javascript复制
var cat = { name: "tom" }
cat.hobby = cat
function deepclone(obj, hash) {
    var newobj = Array.isArray(obj) ? [] : {}
    hash = hash ? hash : new WeakMap()
    if (hash.has(obj)) {
        return hash.get(obj)
    } else {
        hash.set(obj, newobj)
    }
    for (var i in obj) {
        if (obj.hasOwnProperty(i)) {
            if (obj[i] instanceof Object) {
                newobj[i] = deepclone(obj[i], hash)
            } else {
                newobj[i] = obj[i]
            }
        }
    }
    return newobj
}
console.log(deepclone(cat))

0 人点赞