目录
一、知识前提
1、数据类型:
- 基础类型:Number、String、Boolean、Null、Undefined。变量是直接按值存放在栈内存中的简单数据段,可以直接访问。
- 引用类型:存放在堆内存中的对象,变量保存的是一个指针,这个指针指向另一个位置。当需要访问引用类型(如对象Object,数组Array等)的值时,首先从栈中获得该对象的地址指针,然后再从堆内存中取得所需的数据。
2、赋值、浅拷贝与深拷贝的区别
方式 | 是否与原数据指向同一对象 | 第一层数据为基础类型 | 原数据包含子对象 |
---|---|---|---|
赋值 | 是 | 改变、原数据改变 | 改变、原数据改变 |
浅拷贝 | 否 | 改变、原数据不改变 | 改变、原数据改变 |
深拷贝 | 否 | 改变、原数据不改变 | 改变、原数据不改变 |
二、Object 的拷贝
2.1、Object.assign(obj) —— 浅拷贝
拷贝的是(可枚举)属性值
假如源值是一个对象的引用,它仅仅会复制其引用值。
let obj1 = { a: 0 , b: { c: 0 , d: 0}};
let obj2 = Object.assign({}, obj1);
console.log(JSON.stringify(obj2));
// { a: 0, b: { c: 0, d: 0}}
// 修改属性a的 —— 第一层数据为基础类型
obj1.a = 1;
console.log(JSON.stringify(obj1));
// { a: 1, b: { c: 0, d: 0}}
console.log(JSON.stringify(obj2));
// { a: 0, b: { c: 0, d: 0}}
obj2.a = 2;
console.log(JSON.stringify(obj1));
// { a: 1, b: { c: 0, d: 0}}
console.log(JSON.stringify(obj2));
// { a: 2, b: { c: 0, d: 0}}
// 修改属性b中c的值,则因是引用值,obj1,obj2都会修改
obj2.b.c = 3;
console.log(JSON.stringify(obj1));
// { a: 1, b: { c: 3, d: 0}}
console.log(JSON.stringify(obj2));
// { a: 2, b: { c: 3, d: 0}}
let obj1 = { a: 0 , b: { c: 0, d: 0}};
let obj2 = Object.assign({}, obj1);
obj2.a = 1;
obj2.b = { c: 1, d: 1 }; //obj2.b的指针指向了{ c: 1, d: 1 },而不是obj1.b的{ c: 0, d: 0},因此不会修改obj1.b的值
console.log(JSON.stringify(obj1));
console.log(JSON.stringify(obj2));
// {"a":0,"b":{"c":0,"d":0}}
// {"a":1,"b":{"c":1,"d":1}}
参考:
https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Object/assign
2.2、{…obj} —— 浅拷贝
ES6的语法,用于取出参数对象中的所有可遍历属性,拷贝到当前对象之中
等同于Object.assign()
2.3、JSON.parse(JSON.stringify(obj)) —— 深拷贝
obj1 = { a: 0 , b: { c: 0}};
let obj3 = JSON.parse(JSON.stringify(obj1));
obj1.a = 4;
obj1.b.c = 4;
console.log(JSON.stringify(obj3));
// { a: 0, b: { c: 0}}
不足之处:
- 对于正则表达式类型、函数类型等无法进行深拷贝(而且会直接丢失相应的值)
- 无法拷贝原对象原型链上的属性和方法
参考:
https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/JSON/stringify
https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/JSON/parse
三、Array的拷贝
3.1、array.slice() —— 浅拷贝
let array_b = [0,1,2,3];
let array_r = [{a:1,b:1},{a:2,b:2}];
let c_array_b = array_b.slice();
let c_array_r = array_r.slice();
console.log(JSON.stringify(c_array_b));
// [0,1,2,3]
console.log(JSON.stringify(c_array_r));
// [{"a":1,"b":1},{"a":2,"b":2}]
// 修改1
c_array_b[0] = 10;
console.log(JSON.stringify(array_b));
// [0,1,2,3]
console.log(JSON.stringify(c_array_b));
// [10,1,2,3]
// 修改2
c_array_r[0].a = 10;
console.log(JSON.stringify(array_r));
// [{"a":10,"b":1},{"a":2,"b":2}]
console.log(JSON.stringify(c_array_r));
// [{"a":10,"b":1},{"a":2,"b":2}]
// 修改3
c_array_r[0] = {a:10,b:10}; // c_array_r[0]的指针指向了{a:10,b:10},而不是array_r[0]的{"a":1,"b":1},因此不会修改array_r[0]的值
console.log(JSON.stringify(array_r));
// [{"a":1,"b":1},{"a":2,"b":2}]
// [{"a":10,"b":1},{"a":2,"b":2}] 存现这种结果是因为执行了修改2的语句
console.log(JSON.stringify(c_array_r));
// [{"a":10,"b":10},{"a":2,"b":2}]
3.2、array.concat() —— 浅拷贝
与array.slice() 基本相同
let array_b = [0,1,2,3];
let array_r = [{a:1,b:1},{a:2,b:2}];
let c_array_b = array_b.concat();
let c_array_r = array_r.concat();
console.log(JSON.stringify(c_array_b));
// [0,1,2,3]
console.log(JSON.stringify(c_array_r));
// [{"a":1,"b":1},{"a":2,"b":2}]
// 修改1
c_array_b[0] = 10;
console.log(JSON.stringify(array_b));
// [0,1,2,3]
console.log(JSON.stringify(c_array_b));
// [10,1,2,3]
// 修改2
c_array_r[0].a = 10;
console.log(JSON.stringify(array_r));
// [{"a":10,"b":1},{"a":2,"b":2}]
console.log(JSON.stringify(c_array_r));
// [{"a":10,"b":1},{"a":2,"b":2}]
// 修改3
c_array_r[0] = {a:10,b:10}; // c_array_r[0]的指针指向了{a:10,b:10},而不是array_r[0]的{"a":1,"b":1},因此不会修改array_r[0]的值
console.log(JSON.stringify(array_r));
// [{"a":1,"b":1},{"a":2,"b":2}]
// [{"a":10,"b":1},{"a":2,"b":2}] 存现这种结果是因为执行了修改2的语句
console.log(JSON.stringify(c_array_r));
// [{"a":10,"b":10},{"a":2,"b":2}]
3.3、[…array] —— 浅拷贝
let array_b = [0,1,2,3];
let array_r = [{a:1,b:1},{a:2,b:2}];
let c_array_b = [...array_b];
let c_array_r = [...array_r];
console.log(JSON.stringify(c_array_b));
// [0,1,2,3]
console.log(JSON.stringify(c_array_r));
// [{"a":1,"b":1},{"a":2,"b":2}]
// 修改1
c_array_b[0] = 10;
console.log(JSON.stringify(array_b));
// [0,1,2,3]
console.log(JSON.stringify(c_array_b));
// [10,1,2,3]
// 修改2
c_array_r[0].a = 10;
console.log(JSON.stringify(array_r));
// [{"a":10,"b":1},{"a":2,"b":2}]
console.log(JSON.stringify(c_array_r));
// [{"a":10,"b":1},{"a":2,"b":2}]
// 修改3
c_array_r[0] = {a:10,b:10}; // c_array_r[0]的指针指向了{a:10,b:10},而不是array_r[0]的{"a":1,"b":1},因此不会修改array_r[0]的值
console.log(JSON.stringify(array_r));
// [{"a":1,"b":1},{"a":2,"b":2}]
// [{"a":10,"b":1},{"a":2,"b":2}] 存现这种结果是因为执行了修改2的语句
console.log(JSON.stringify(c_array_r));
// [{"a":10,"b":10},{"a":2,"b":2}]
3.4、JSON.parse(JSON.stringify(array)) —— 深拷贝
同 2.3节