JavaScript浅拷贝与深拷贝

定义

浅拷贝(shallow copy):只复制指向某个对象的指针,而不复制对象本身,新旧对象共享一块内存。
深拷贝(deep copy):复制并创建一个一摸一样的对象,不共享内存,修改新对象,旧对象保持不变。

示例

1. 基本数据类型


// 基本数据类型

var a = 1;
var b = a;
b = 2;

console.log(a); // 1
console.log(b); // 2
  • 基本数据类型(number、string、boolean,null、undefined)进行赋值时,按值传递。
  • 当 b=a 赋值时,栈内存会新开辟一个内存,所以当修改b时,不会影响a。

变量a在内存中的存储

b=a赋值之后内存的存储

注:基本数据类型,变量名和值都存储在栈内存中。

2. 引用数据类型


// 示例1 -- 数组
var a = [1, 2, 3, 4];
var b = a;

console.log(a === b);
a[0] = 0; // 修改数组a的值
console.log(a);
console.log(b);

// 示例2 -- 对象
var obj = {
    name: 'Lucy',
    age: 17
}
var newObj = obj;
newObj.name = 'Andy'; // 修改新的对象的属性name的值
console.log(obj);
console.log(newObj);
示例1输出结果

示例2输出结果
  • 数组和对象进行赋值操作时,是按引用传值;
  • 当赋值时,复制的仅是指针地址而已;
  • 浅拷贝和深拷贝都只针对于像Object, Array这样的复杂对象;
  • 区别:浅拷贝只复制对象的第一层属性、深拷贝可以对对象的属性进行递归复制。

数组赋值过程

注: 引用数据类型,变量名存在栈内存中,值存在于堆内存中,栈内存提供一个引用的地址指向堆内存中的值。

实现

1. 浅拷贝

① 对数组或对象进行普通的赋值。

var a = [1, 2, 3, 4];
var b = a;

console.log(a === b); // true
a[0] = 0; // 修改数组a的值
console.log(a); // [0, 2, 3, 4]
console.log(b); // [0, 2, 3, 4]

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

Object.assign(target, ...sources)
参数:
target -- 目标对象
source -- 源对象
返回值
目标对象

var obj = {
    student: {
        name: 'Lucy',
        age: 17
    }
}

var newObj = Object.assign({}, obj);
newObj.student.name = "Andy";
console.log(obj);
console.log(newObj);
Object.assign() 多层属性时赋值
注:Object.assign() 只能实现非递归属性的一层的深度拷贝。
var obj1 = {
    name: 'Lucy',
    age: 17
};
var obj2 = Object.assign({}, obj1);
obj2.name = 'Andy';
console.log(obj1);
console.log(obj2);
Object.assign()实现一层属性深拷贝

2. 深拷贝

(1)数组

① slice() -- 从已有的数组中返回选定的元素。

arrayObject.slice(start,end)
参数
start -- 数组起始位置,包括此位置,如果负数,从数组尾部算起。
end -- 截取的结束位置,不包括此位置,如果负数,从数组尾部算起,如果省略,默认到结束所有元素。
返回值
新的数组 [start, end)

var arr1 = [1, 2, 3, 4];
var arr2 = arr1.slice(0);
arr2[0] = 0;
console.log(arr1);
console.log(arr2);
slice()实现一层属性深拷贝

② concat() ---- 用于连接两个或多个数组。

arrayObject.concat(arrayX,arrayX,......,arrayX)
参数
arrayX -- 可以为具体的值或数组对象。
返回值
新的数组。


var arr1 = [1, 2, 3, 4];
var arr2 = arr1.concat();
arr2[0] = 0;
console.log(arr1);
console.log(arr2);
concat()实现一层属性深拷贝
注:concat()和slice()函数只能实现数组仅有一层不存在递归属性的情况;如果数组存在递归属性,则此两种方法只能实现的为浅拷贝。
var arr1 = [1, [2, 3], 4];
var arr2 = arr1.slice();
arr2[1][0] = [5, 6];
console.log(arr1);
console.log(arr2);
多层属实时
(2)对象

① 手动复制遍历每个属性

var obj1 = {
    name: "Lucy",
    age: 17
};

var obj2 = new Object();
obj2.name = obj1.name;
obj2.age = obj1.age;
obj2.name = "Andy";

console.log(obj1);
console.log(obj2);
手动复制

② Object.assign() -- 仅针对对象只有一层属性,不存在递归属性。
③ 转成JSON

JSON.stringify()把对象转成字符串;JSON.parse() 把字符串转成新的对象。

 var obj1 = {
    student: {
        name: 'Lucy',
        age: 17
    }
}
var obj2 = JSON.parse(JSON.stringify(obj1));
obj2.student.name = 'Andy';

console.log(obj1 === obj2);
console.log(obj1);
console.log(obj2);
JSON实现深拷贝

④ 递归

 var obj1 = {
    student: {
        name: 'Lucy',
        age: 17
    }
}

function deepCopy(obj) {
    var result = {};
    for (var name in obj) {
        if (typeof obj[name] === 'object') {
            result[name] = deepCopy(obj[name]);
        } else {
            result[name] = obj[name];
        }
    }
    return result;
}

var obj2 = deepCopy(obj1);
obj2.student.name = 'Andy';

console.log(obj1 === obj2);
console.log(obj1);
console.log(obj2);
递归实现深拷贝

⑤ JQuery的extend()方法 -- 用于将一个或多个对象的内容合并到目标对象。

$.extend( [deep ], target, object1 [, objectN ] )
参数
deep - 是否进行深度拷贝,默认为false
target- Object类型 目标对象,其他对象的成员属性将被附加到该对象上。
object1 - Object类型 第一个被合并的对象
返回值
目标对象

var arr1 = [1, 2, [3, 4], 5];
var arr2 = $.extend(true, [], arr1);
arr1[2][0] = [7, 8];
console.log(arr1);
console.log(arr2);
$.extend()实现深拷贝

在默认情况下,通过$.extend()合并操作不是递归的(浅拷贝);如果第一个对象的属性本身是一个对象或数组,那么它将完全用第二个对象相同的key重写一个属性。这些值不会被合并。然而,如果将 true 作为该函数的第一个参数,那么会在对象上进行递归的合并(深拷贝)。

浅拷贝(false 默认):如果第二个参数对象有的属性第一个参数对象也有,那么不会进行相同参数内部的比较,直接将第一个对象的相同参数覆盖。

深拷贝(true):如果第二个参数对象有的属性第一个参数对象也有,还要继续在这个相同的参数向下一层找,比较相同参数的对象中是否还有不一样的属性,如果有,将其继承到第一个对象,如果没有,则覆盖。

var obj1 = {
    student: {
        name: 'Lucy',
        age: '17',
        gender: 'female'
    },
    grade: 1
}
var obj2 = {
    student: {
        name: 'Andy',
        age: 18,
    },
    grade: 2,
    hobby: 'football'
}

console.log($.extend(obj1, obj2));
extend()浅拷贝
var obj1 = {
    student: {
        name: 'Lucy',
        age: '17',
        gender: 'female'
    },
    grade: 1
}
var obj2 = {
    student: {
        name: 'Andy',
        age: 18,
    },
    grade: 2,
    hobby: 'football'
}

console.log($.extend(true, obj1, obj2));
extend()深拷贝

参考

JS深拷贝与浅拷贝的区别,实现深拷贝的几种方法
JavaScript中浅拷贝和深拷贝的区别和实现
关于JavaScript的浅拷贝和深拷贝
JQuey.extend()的深拷贝和浅拷贝详细讲解

你可能感兴趣的:(JavaScript浅拷贝与深拷贝)