十一、聊一聊js中实现数组拷贝的常用方法

138 阅读4分钟

一、数组赋值

1、要点

用数组直接赋值的方式实现数组的拷贝,改变拷贝后的数组的元素,被拷贝的数组的元素也会发生改变


var arr1 = ["red","yellow","blue"];

var arr2 = arr1;

arr2[1] = "green";

console.log("数组的原始值:" + arr1 ); // 数组的原始值:red,green,blue

console.log("数组的新值:" + arr2); // 数组的新值:red,green,blue

2、引用数据类型

Object 、Array 、Function

javascript的引用数据类型是保存在堆内存中的对象。

  与其他语言的不同是,你不可以直接访问堆内存空间中的位置和操作堆内存空间。只能操作对象在栈内存中的引用地址。

  所以,引用类型数据在栈内存中保存的实际上是对象在堆内存中的引用地址。通过这个引用地址可以快速查找到保存中堆内存中的对象。

  


var obj1 = new Object();

var obj2 = obj1;

obj2.name = "李雷";

console.log(obj1.name); // 李雷

image

二、数组浅拷贝

Array拷贝方法总结
Array.slicefor() 、whilemap、 filter
Array.cancatArray.reduce()Object.assign() Es6
扩展运算符 Es6Array.from() Es6 JSON.parse(JSON.stringify())(深拷贝)

1、ES5 Array复制方法总结

(1)Array.slice(浅拷贝)

slice 方法根据我们指定的start、end的index从原数组中返回一个浅拷贝的数组。


[1, 2, 3, 4, 5].slice(0, 3);

// [1, 2, 3]

// Starts at index 0, stops at index 3

测试:当不给定参数时,就返回了原数组的拷贝


let arr = [2, 4, 6, 8];

let arr1 = arr.slice();

console.log(arr1); // (4) [2, 4, 6, 8]

arr[0] = 'a';

console.log(arr, arr1); // (4) ["a", 4, 6, 8] (4) [2, 4, 6, 8]

(2)Array.cancat(浅拷贝)

concat将数组与值或其他数组进行组合。


[1, 2, 3].concat(4); // [1, 2, 3, 4]

测试:


let arr = ['a', 2, 4];

Array.prototype.clone = function () {

return [].concat(this);

}

let arr1 = arr.clone();

console.log(arr1) // (3) ["a", 2, 4]

如果我们不指定参数或者提供一个空数组作为参数,就可以进行浅拷贝。


let arr = [1, 2, 3];

let arr1 = [1, 2, 3].concat();

let arr2 = [1, 2, 3].concat([]);

console.log(arr1, arr2); // (3) [1, 2, 3] (3) [1, 2, 3]

(3)遍历复制数组:for() 、while、map、 filter、 reduce

for()循环(浅拷贝)

测试1:


let arr = ['a', 2, 4];

Array.prototype.clone = function () {

let a = [];

for (let i = 0, l = this.length; i<l; i++) {

a.push(this[i]);

}

return a;

}

let arr2 = arr.clone();

console.log(arr2) // (3) ["a", 2, 4]

测试2:


let numbers = [1, 2, 3];

let numbersCopy = [];

for (let i = 0; i < numbers.length; i++) {

numbersCopy[i] = numbers[i];

}

console.log(numbersCopy) // (3) [1, 2, 3]

while()循环(浅拷贝)


let numbers = [1, 2, 3];

let numbersCopy = [];

let i = -1;

while (++i < numbers.length) {

numbersCopy[i] = numbers[i];

}

console.log(numbersCopy) // (3) [1, 2, 3]

Array.map(浅拷贝)

当我们使用map方法时,需要给出一个callback函数用于处理当前的数组,并返回一个新的数组元素。


let numbers = [1, 2, 3];

let numbersCopy = numbers.map(x => x);

console.log(numbersCopy) // (3) [1, 2, 3]

Array.filter(浅拷贝)

Array.filter方法同样会返回一个新数组,但是并不一定是返回同样长度的,这和我们的过滤条件有关。


let numbers = [1, 2, 3];

let numbersCopy = numbers.filter(x => x);

console.log(numbersCopy) // (3) [1, 2, 3]

Array.reduce(浅拷贝)

其实用reduce来拷贝数组并没有展示出它的实际功能,但是我们还是要将其能够拷贝数组的能力说一下的。


let numbers = [1, 2, 3];

let numbersCopy = numbers.reduce((newArray, element) => {

newArray.push(element);

return newArray;

}, []);

console.log(numbersCopy) // (3) [1, 2, 3]

reduce() 方法对数组中的每个元素执行一个由您提供的reducer函数,将其结果汇总为单个返回值。

2、ES6 方法总结

(1)Object.assign() 浅拷贝

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


let arr = ['张三', 24, '前端工程师', '男'];

let arr1 = [];

Object.assign(arr1, arr);

console.log(arr1); // (4) ["张三", 24, "前端工程师", "男"]

(2)扩展运算符(浅拷贝)


let arr = [1, 2, 3];

let arr1 = [...arr];

console.log(arr1) // (3) [1, 2, 3]

这个方法不能有效的拷贝多维数组。数组/对象值的拷贝是通过引用而不是值复制。

(3)Array.from(浅拷贝)

可以将任何可迭代对象转换为数组。给一个数组返回一个浅拷贝。


console.log(Array.from('foo')); // (3) ["f", "o", "o"]

let numbers = [1, 2, 3];

let numbersCopy = Array.from(numbers);

console.log(numbersCopy) // (3) [1, 2, 3]

三、数组深拷贝

JSON.parse & JSON.stringify(深拷贝)

  • JSON.stringify将一个对象转成字符串

  • JSON.parse将转成的字符串转回对象

将它们组合起来可以将对象转换成字符串,然后反转这个过程来创建一个全新的数据结构。


let nestedNumbers = [[1], [2]];

let numbersCopy = JSON.parse(

JSON.stringify(nestedNumbers)

);

numbersCopy[0].push(300);

console.log(nestedNumbers, numbersCopy);

// [[1], [2]]

// [[1, 300], [2]]

这个可以安全地拷贝深度嵌套的对象/数组

几种特殊情况:

(1)如果obj里面有时间对象,则JSON.stringify后再JSON.parse的结果,时间将只是字符串的形式。而不是时间对象

let test = {

name: 'a',

date: [new Date(1536627600000), new Date(1540047600000)],

};

let b = JSON.parse(JSON.stringify(test));

console.log(b)

(2)如果obj里有RegExp、Error对象,则序列化的结果将只得到空对象

const test = {

name: 'a',

date: new RegExp('\\w+'),

};

const copyed = JSON.parse(JSON.stringify(test));

test.name = 'test';

console.log(test, copyed)

(3)如果obj里有函数,undefined,则序列化的结果会把函数或 undefined丢失

const test = {

name: 'a',

date: function help() {

console.log('message')

},

};

const copyed = JSON.parse(JSON.stringify(test));

test.name = 'test'

console.log(test, copyed)

11-4.png

(4)如果obj里有NaN、Infinity和-Infinity,则序列化的结果会变成null
(5)JSON.stringify()只能序列化对象的可枚举的自有属性

例如 如果obj中的对象是有构造函数生成的, 则使用JSON.parse(JSON.stringify(obj))深拷贝后,会丢弃对象的constructor;


function Person(name) {

this.name = name;

}

const lilei = new Person('lilei');

const test = {

name: 'a',

date: lilei,

};

const copyed = JSON.parse(JSON.stringify(test));

test.name = 'test'

console.log(test, copyed)