一、数组
1、复制
var arr1 = [1, 2, 3]
var arr2 = arr1
console.log(arr1) // [1, 2, 3]
console.log(arr2) // [1, 2, 3]
arr2[0] = 5
console.log(arr1) // [5, 2, 3]
console.log(arr2) // [5, 2, 3]
浅copy,两个数组指向同一个地址,一个改变了数组的值,另一个也随之改变。这样的copy并非我们需要的,我们需要的是深copy,两个数组值一样,但是指向不同的地址,一个数组的改变不会影响另一个数组。
方法一:for遍历
var arr1 = [1, 2, 3]
var arr2 = []
for (var i = 0; i < arr1.length; i++) {
arr2.push(arr1[i])
}
console.log(arr1) // [1,2,3]
console.log(arr2) // [1,2,3]
arr2[0] = 5
console.log(arr1) // [1,2,3]
console.log(arr2) // [5,2,3]
方法二:slice方法(第一层copy)
var arr1 = [1, 2, 3]
var arr2 = arr1.slice(0)
console.log(arr1) // [1,2,3]
console.log(arr2) // [1,2,3]
arr2[0] = 5
console.log(arr1) // [1,2,3]
console.log(arr2) // [5,2,3]
var arr3 = [1, 2, [3,4], {a: 5}]
var arr4 = arr3.concat()
arr4[2].push(6)
arr4[3].a = 7
console.log(arr3) // [1,2,[3,4,6], {a: 7}]
console.log(arr4) // [1,2,[3,4,6], {a: 7}]
arr3 中含有数组 [3,4] 和对象 {a:5},如果我们直接修改数组和对象,不会影响 arr3,但是我们修改数组 [3,4] 或对象 {a:5} 时,发现 arr3 也发生了变化。所以,slice 只是对数组的第一层进行深拷贝
方法三:concat 方法(第一层copy)
var arr1 = [1, 2, 3]
var arr2 = arr1.concat()
console.log(arr1) // [1,2,3]
console.log(arr2) // [1,2,3]
arr2[0] = 5
console.log(arr1) // [1,2,3]
console.log(arr2) // [5,2,3]
var arr3 = [1, 2, [3,4], {a: 5}]
var arr4 = arr3.slice()
arr4[2].push(6)
arr4[3].a = 7
console.log(arr3) // [1,2,[3,4,6], {a: 7}]
console.log(arr4) // [1,2,[3,4,6], {a: 7}]
arr3 中含有数组 [3,4] 和对象 {a:5},如果我们直接修改数组和对象,不会影响 arr3,但是我们修改数组 [3,4] 或对象 {a:5} 时,发现 arr3 也发生了变化。所以,concat 只是对数组的第一层进行深拷贝。
方法四:扩展运算符(es6)
var arr1 = [1, 2, 3]
var arr2 = [...arr1]
console.log(arr1) // [1,2,3]
console.log(arr2) // [1,2,3]
arr2[0] = 5
console.log(arr1) // [1,2,3]
console.log(arr2) // [5,2,3]
const arr1 = [1,2,3,4,5,[6,7,8]];
const cloneArray = [...arr1];
cloneArray[0] = 0;
cloneArray[5].push(9);
console.log(arr1); // [1,2,3,4,5,[6,7,8,9]]
... 实现的是对象第一层的深拷贝。后面的只是拷贝的引用值。
方法五:JSON.parse、JSON.stringify
var arr1 = [1, 2, 3]
var arr2 = JSON.parse(JSON.stringify(arr1))
console.log(arr1) // [1, 2, 3]
console.log(arr2) // [1, 2, 3]
arr2[0] = 5
console.log(arr1) // [5, 2, 3]
console.log(arr2) // [5, 2, 3]
var obj1 = {
a: 1,
b: 2
}
2、合并
方法一:for遍历(不去重)
思路:遍历其中一个数组,把该数组中的所有元素依次添加到另外一个数组中。
var arr1 = [1, 2, 3]
var arr2 = [2, 3, 4]
for (var i = 0; i < arr1.length; i++) {
arr2.push(arr1[i])
}
console.log(arr1) // [1,2,3]
console.log(arr2) // [1,2,3,2,3,4]
方法二:concat方法(不去重)
var arr1 = [1, 2, 3]
var arr2 = [2, 3, 4]
var arr3 = arr1.concat(arr2)
console.log(arr1) // [1,2,3]
console.log(arr2) // [2,3,4]
console.log(arr3) // [1,2,3,2,3,4]
concat方法连接a、b两个数组后,a、b两个数组的数据不变,同时会返回一个新的数组。这样当我们需要进行多次的数组合并时,会造成很大的内存浪费,所以这个方法肯定不是最好的。
方法三:apply
var arr1 = [1, 2, 3]
var arr2 = [2, 3, 4]
arr1.push.apply(arr1, arr2)
console.log(arr1) // [1,2,3,2,3,4]
console.log(arr2) // // [2,3,4]
// 或者
var arr1 = [1, 2, 3]
var arr2 = [2, 3, 4]
Array.prototype.push.apply(arr1, arr2)
console.log(arr1) // [1,2,3,2,3,4]
console.log(arr2) // // [2,3,4]
一、对象
1、复制
var obj1 = {
a: 1,
b: 2
}
var obj2 = obj1
console.log(obj1); // {a:1,b:2}
console.log(obj2); // {a:1,b:2}
obj2.a = 3
console.log(obj1); // {a:3,b:2}
console.log(obj2); // {a:3,b:2}
浅copy,两个对象指向同一个地址,一个改变了对象的值,另一个也随之改变。这样的copy并非我们需要的,我们需要的是深copy,两个对象值一样,但是指向不同的地址,一个对象的改变不会影响另一个数组。
方法一:for遍历
var obj1 = {
a: 1,
b: 2
}
var obj2 = {}
for (var k in obj1) {
obj2[k] = obj1[k]
}
console.log(obj1);
console.log(obj2);
obj2.a = 3
console.log(obj1);
console.log(obj2);
方法二:JSON.parse、JSON.stringify
var obj1 = {
a: 1,
b: 2
}
var obj2 = JSON.parse(JSON.stringify(obj1))
console.log(obj1); // {a:1,b:2}
console.log(obj2); // {a:1,b:2}
obj2.a = 3
console.log(obj1); // {a:1,b:2}
console.log(obj2);// {a:3,b:2}
但是,这个方法只能适用于一些简单的情况。例如
var obj1 = {
a: 1,
b: 2,
setNum: function () {
console.log('hello world');
}
}
var obj2 = JSON.parse(JSON.stringify(obj1))
console.log(obj1);
console.log(obj2);
obj2.a = 3
console.log(obj1);
console.log(obj2);
image.png
发现在obj2 中,有属性丢失了......因为undefined、function、symbol 会在转换过程中被忽略。。。所以,如果对象中含有一个函数时(很常见),就不能用这个方法进行深拷贝
方法三、递归的方法(适用于数组、对象)
function deepClone(source) {
const targetObj = source.constructor === Array ? [] : {}; // 判断复制的目标是数组还是对象
for (let keys in source) { // 遍历目标
if (source.hasOwnProperty(keys)) {
if (source[keys] && typeof source[keys] === 'object') { // 如果值是对象,就递归一下
targetObj[keys] = source[keys].constructor === Array ? [] : {};
targetObj[keys] = deepClone(source[keys]);
} else { // 如果不是,就直接赋值
targetObj[keys] = source[keys];
}
}
}
return targetObj;
}
const obj1 = {
a: 1,
b: 2,
setNum: function () {
console.log('hello world')
}
}
const obj2 = deepClone(obj1)
console.log(obj1 === obj2)
obj2.a = 3
console.log(obj1);
console.log(obj2);
var arr1 = [1, 2, 3, {a: 'arr1'}]
var arr2 = deepClone(arr1)
console.log(arr1);
console.log(arr2);
arr2[3].a = 'arr2'
console.log(arr1);
console.log(arr2);
方法四、扩展运算符
const obj1 = {a:1,b:{bb:1}};
const cloneObj = {...obj1};
cloneObj.a = 2;
cloneObj.b.bb = 2;
console.log(obj1); // {a:1,b:{bb:2}}
... 实现的是对象第一层的深拷贝。后面的只是拷贝的引用值。
方法五、object.assign
const obj1 = {a:1,b:{bb:1}};
var obj2 = {}
Object.assign(obj2,obj1)
obj2.b.bb = 3
console.log(obj1); // {a:1,b:{bb:3}}
console.log(obj2);// {a:1,b:{bb:3}}
var obj3 = {a:1,b:2}
var obj4 = {}
Object.assign(obj4,obj3)
obj4.a = 5
console.log(obj3); // {a:1,b:2}
console.log(obj4);// {a:5,b:2}
Object.assign() 拷贝的是属性值。假如源对象的属性值是一个指向对象的引用,它也只拷贝那个引用值。
总结:
1、赋值运算符 = 实现的是浅拷贝,只拷贝对象的引用值;
2、JavaScript 中数组和对象自带的拷贝方法都是“首层浅拷贝”;
3、JSON.stringify 实现的是深拷贝,但是对目标对象有要求(非 undefined,function);
4、若想真正意义上的深拷贝,请递归。






网友评论