一、关于基础类型和引用类型
- 基础类型包括 number、boolean、string、null、undefined (ES6 Symbol、BigInt)
- 引用类型包括 Array、Object、Function
基础类型:直接存储在栈中的数据 引用类型:在栈中存储了指针,该指针指向堆中该实体的起始地址
二、浅拷贝和深拷贝
- 拷贝的意思就是复制一份,对于基础数据类型,不存在浅拷贝深拷贝的问题,复制的就是值的内容
- 对于引用数据类型,复制的是指向内容的地址,所以采用复制实现的是浅拷贝
三、值的改变
- 浅拷贝:复制的一份改变内容时,原始的内容会被改变,这是我们不希望看到的
- 深拷贝:复制的和原始的不在有关系,不会互相影响
四、浅拷贝方法
6.扩展运算符
let obj1 = { a: 1, b: { c: 1 } };
let obj2 = { ...obj1 }; // 扩展运算符
obj1.a = 2;
console.log(obj1); // { a: 2, b: { c: 1 } }
console.log(obj2); // { a: 1, b: { c: 1 } }
console.log(obj1 === obj2); // false
let arr = [1, 2, 3];
let arr2 = [...arr];
console.log(arr2); // [1,2,3]
console.log(arr === arr2); // false
1、简单复制
// 对象赋值 obj2改变属性会同时修改obj1 因为复制的地址指向同一个内容区
var obj1 = {
'name': 'zhangsan',
'age': '18',
'language': [1, [2, 3], [4, 5]],
};
var obj2 = obj1;
obj2.name = "lisi";
obj2.language[1] = ["二", "三"];
console.log('obj1', obj1) // obj1 { name: 'lisi', age: '18', language: [ 1, [ '二', '三' ], [ 4, 5 ] ] }
console.log('obj2', obj2) // obj2 { name: 'lisi', age: '18', language: [ 1, [ '二', '三' ], [ 4, 5 ] ] }
2、Object.assign()
var obj = {
a: {
a: "kobe",
b: 39
}
};
var assignObj = Object.assign({}, obj);
assignObj.a.a = "ccc"; //会改变obj.a.a 的值
console.log(obj); // { a: { a: 'ccc', b: 39 } }
console.log(assignObj); // a: { a: 'ccc', b: 39 } }
// assign对第一层实现的是深拷贝
let obj = {
username: 'kobe'
};
let obj2 = Object.assign({}, obj);
obj2.username = 'ccc';
console.log(obj);//{username: "kobe"}
console.log(obj2); // { username: 'ccc' }
3、slice()
slice到底是深拷贝还是浅拷贝其实取决于数组中的元素,如果数组中的元素是基本数据类型,那就是深拷贝,如果是引用类型,那就是浅拷贝,可以理解成slice只能复制引用类型的地址,那当其他因素要改变当前的数组值时,值会发生改变
let arr = [1, 3, { username: ' kobe' }];
let arr3 = arr.slice();
arr3[2].username = 'ccc'
console.log(arr); //[ 1, 3, { username: 'ccc' } ]
console.log(arr3); // [ 1, 3, { username: 'ccc' } ]
// 基础类型,slice不会修改原数组
let arr = [1, 3, 4];
let arr3 = arr.slice();
arr3[2] = 2;
console.log(arr); //[ 1, 3, 4 ]
console.log(arr3); // [ 1, 3, 2 ]
4、concat()
let arr = [1, 3, { username: 'kobe' }];
let arr2 = arr.concat();
arr2[2].username = 'ccc';
console.log(arr); // [ 1, 3, { username: 'ccc' } ]
console.log(arr2); // [ 1, 3, { username: 'ccc' } ]
// 基础类型,concat不会修改原数组
let arr = [1, 3, 4];
let arr2 = arr.concat();
arr2[2] = 2;
console.log(arr); // [ 1, 3, 4 ]
console.log(arr2); // [ 1, 3, 2 ]
关于Array的slice和concat方法的补充说明:Array的slice和concat方法不修改原数组,只会返回一个浅复制了原数组中的元素的一个新数组。 原数组的元素会按照下述规则拷贝:
1.该元素是个对象引用(不是实际的对象),slice 会拷贝这个对象引用到新的数组里。两个对象引用都引用了同一个对象。如果被引用的对象发生改变,则新的和原来的数组中的这个元素也会发生改变。
2.对于字符串、数字及布尔值来说(不是 String、Number 或者 Boolean 对象),slice 会拷贝这些值到新的数组里。在别的数组里修改这些字符串或数字或是布尔值,将不会影响另一个数组。
5、浅拷贝
// 浅拷贝 基础数据不变,引用类型会变
var obj1 = {
'name': 'zhangsan',
'age': '18',
'language': [1, [2, 3], [4, 5]],
};
var obj3 = qianCopy(obj1);
obj3.name = "lisi";
obj3.language[1] = ["二", "三"];
function qianCopy(obj) {
let resObj = {};
for (let key in obj) {
if (obj.hasOwnProperty(key)) {
resObj[key] = obj[key]; //基础的深拷贝,引用的复制的地址
}
}
return resObj;
}
console.log('obj1', obj1) // obj1 {name: 'zhangsan',age: '18',language: [ 1, [ '二', '三' ], [ 4, 5 ] ]}
console.log('obj3', obj3) // obj3 { name: 'lisi', age: '18', language: [ 1, [ '二', '三' ], [ 4, 5 ] ] }
五、深拷贝方法
1、JSON.parse(JSON.stringify())
let arr = [1, 3, { username: ' kobe' }];
let arr4 = JSON.parse(JSON.stringify(arr));
arr4[2].username = 'ccc';
console.log(arr); //[ 1, 3, { username: ' kobe' } ]
console.log(arr4); //[ 1, 3, { username: 'ccc' } ]
2、递归方法
// 简单的递归
function deepCopy(target) {
let res = Array.isArray(target) ? [] : {};
for (let key in target) {
if (typeof target[key] === 'object') {
res[key] = deepCopy(target[key]);
} else {
res[key] = target[key]
}
}
return res;
}
//对象拷贝
var A = {
name: "martin",
data: { num: 10 },
say: function () {
console.log("say");
}
};
let B = deepCopy(A);
B.data.num = 18;
console.log(A); // { name: 'martin', data: { num: 10 }, say: [Function: say] }
console.log(B); //{ name: 'martin', data: { num: 18 }, say: [Function: say] }
//数组拷贝
let arr = [1, 3, { username: ' kobe' }];
let arr3 = deepCopy(arr);
arr3[2].username = 'ccc'
console.log(arr); //[ 1, 3, { username: 'kobe' } ]
console.log(arr3); // [ 1, 3, { username: 'ccc' } ]
/**
* 递归实现深拷贝 数组和对象一起
* @param {*} target
*/
//检测数据类型
function checkType(target) {
return Object.prototype.toString.call(target).slice(8, -1)
}
//实现深拷贝克隆 对象或者数组
function clone(target) {
//返回值
let result;
//检测类型
let targetType = checkType(target);
if (targetType === 'Object') {
result = {};
} else if (targetType === 'Array') {
result = [];
} else {
//基础类型或者函数
result = target;
}
for (let key in target) {
//如果是对象/数组嵌套
if (checkType(target[key]) === 'Object' || checkType(target[key]) === 'Array') {
result[key] = clone(target[key]);
} else {
result[key] = target[key];
}
}
return result;
}
//数组拷贝
let arr = [1, 3, { username: ' kobe' }];
let arr3 = clone(arr);
arr3[2].username = 'ccc'
console.log(arr); //[ 1, 3, { username: 'kobe' } ]
console.log(arr3); // [ 1, 3, { username: 'ccc' } ]
3、函数库lodash
var _ = require('lodash');
var obj1 = {
a: 1,
b: { f: { g: 1 } },
c: [1, 2, 3]
};
var obj2 = _.cloneDeep(obj1);
console.log(obj1.b.f === obj2.b.f);
// false