JavaScript-浅拷贝与深拷贝的方式
1. 直接赋值,值拷贝和引用拷贝
** js中的基本数据类型:String Number Boolean Null Undefined,在赋值的过程中都是值拷贝 **
var a1 = 1;
var b1 = a1;
console.log(a1 == b1); //true
console.log(a1 === b1); //true
** js中的对象数据类型:Object Array Function Map Set,在赋值过程中都是引用拷贝 **
var a2 = {
name: '张三', age: 18, fans: function() {
return 0}};
var b2 = a2;
a2.age = 28;
console.log(a2 == b2); //true
console.log(a2 === b2); //true
console.log(b2.age);
2.1 Object.assign() [ES6]
var c2 = Object.assign(a2);
// 测试 Object.assign(a2, {}),即源对象为空对象,仍然为浅拷贝
a2.age = 30;
console.log(c2 === a2); //true
console.log(c2.age); //30
关于Object.assign()
Object.assign() 方法用于对象的合并,将源对象(source)的所有可枚举属性,复制到目标对象(target)。
Object.assign()方法的第一个参数是目标对象,后面的参数都是源对象。
const target = { a: 1 };
const source1 = { b: 2 };
const source2 = { c: 3 };
Object.assign(target, source1, source2);
console.log(target); // {a:1, b:2, c:3}
如果目标对象与源对象有同名属性,或多个源对象有同名属性,则后面的属性会覆盖前面的属性。
const target = { a: 1, b: 1 };
const source1 = { b: 2, c: 2 };
const source2 = { c: 3 };
Object.assign(target, source1, source2);
如果只有一个参数,Object.assign()会直接返回该参数。(**即实现浅拷贝)
const obj = {a: 1};
Object.assign(obj) === obj // true
如果该参数不是对象,则会先转成对象,然后返回。(由于undefined和null无法转成对象,所以如果它们作为参数,就会报错。)
typeof Object.assign(2) // "object"
3. 深拷贝
3.1 array.slice(0)
var a3 = [1, 2, 3];
var b3 = a3.slice(0);
console.log(a3 == b3); // false
array.slice(0)返回一个新数组
slice(start, end)截取数组内元素
数组名.slice(start, end) 表示从数组中截取 start(包括)位置索引到end(不包括)
即左闭右开[...)
如果不写end,就会截取到数组的最后
该方法不会改变原数组结构,只是将新提取的元素作为一个新数组返回
3.2 array.concat()
var a4 = [1, 2, 3, 4];
var b4 = a4.concat();
console.log(a4 == b4); //false
concat() 合并数组并返回新的数组
var arr1 = [1, 2];
var arr2 = [3, 4];
var arr3 = [5, 6, 7];
var arr = arr1.concat(arr2, arr3);
console.log(arr) // [1, 2, 3, 4, 5, 6, 7];
3.3 JSON.parse(JSON.stringify(oldobj))
var a5 = {
fans: function() {
}, gender: undefined, name: '123', date: new Date()};
var b5 = JSON.parse(JSON.stringify(a5));
console.log(b5); // {name: '123', date: '2022-07-14T14:23:13.720Z'}
console.log(b5 == a5); //false
对象中的属性值如果为函数func或者值为underfined,这个属性会消失;
如果值是时间对象,则转换为字符串格式
3.4 for…in 或 for循环
var b6 = {
};
for(var key in a2) {
console.log(key);
console.log(a2[key]);
b6[key] = a2[key];
}
console.log(a2, b6); // {name: '张三', age: 30, fans: ƒ}
3.5 Object.assign({}, oldObj) [ES6]
在1.2中已说明Object.assign()的用法
var b8 = Object.assign({
}, a2);
console.log(b8 == a2); // false
3.6 Object.create(oldObj);???
待研究
var b9 = Object.create({
name: '张三'});
console.log(b9);
3.7 扩展运算符 [ES6]
var b10 = {
...a2 };
console.log(b10);
console.log(b10 == a2);
3.8 arr.map(val => {return val})
// arr.map()方法遍历处理数组中每个数据并返回新数组,如不作特殊处理则直接返回与原数组内容相同的新数组
var a13 = [1, 2, 3, 4, 5];
var b13 = a13.map(val => {
return val});
console.log(b13);
console.log(a13 == b13); // false
3.9 深拷贝 Array.from(oldArr) [ES6]
// Array.from() 方法对一个类数组或者可迭代对象创建一个新的数组
var a14 = [1, 5, 6, 5, 7];
var b14 = Array.from(a14);
console.log(a14);
console.log(a14 == b14); // false
3.10 完全深拷贝 自定义函数deepClone()
var a11 = {
name: '张三',
age: 22,
girls: [1, 2, 3, {
name: '李四'}]
}
var b11 = {
... a11};
// 如果对象中某个值是引用数据类型(girls),使用深拷贝模式其实也只拷贝了引用地址,所以还是浅拷贝
b11.girls.push(3);
console.log(a11.girls); // [1, 2, 3, {name: '李四'}, 3]
// 即a11的girls和b11的girls相互影响
// 解决办法:自行封装函数,完全深拷贝
var a11 = {
name: '张三',
age: 22,
girls: [1, 2, 3, {
name: '李四'}]
}
var b12 = deepClone(a11); // 完全深拷贝
a11.girls.push('王五');
console.log(b12.girls); // 不受影响
function deepClone(oldObj) {
//先确定新创建一个数组还是对象,判断对象的数据类型,Array还是Object
var newObj = Array.isArray(oldObj) ? [] : {
};
// 确定oldObj 不为空,而是一个对象
if(oldObj && typeof oldObj === 'object') {
for(var key in oldObj) {
// 循环获取键名和键值
// 键名 key
// 键值 oldObj[key]
if(oldObj[key] && typeof oldObj[key] === 'object') {
//判断oldObj 的子元素 oldObj[key]是否为对象,如果是,递归复制
newObj[key] = deepClone(oldObj[key]);
} else {
//不是引用数据类型,就简单复制
newObj[key] = oldObj[key];
}
}
}
return newObj;
}