(以下是从各种网站、同学处整理的前端实习面试题,答案也为自己整理,有错误的话欢迎指正)
2、复制一个数组有哪些方法?哪个方法最好?
【在说明复制方法之前,首先说明一下什么是深拷贝,什么是浅拷贝】
- 浅拷贝:浅拷贝只是复制了内存地址,如果原地址中的对象改变了,浅拷贝出来的对象也会相应改变。
- 深拷贝:开辟了一块新的内存存放地址和地址指向的对象,原地址的任何对象改变了,深拷贝出来的对象不变。
①扩展运算符[...](深拷贝/浅拷贝)
扩展运算符是ES6的新特性,通过扩展运算符对数组复制有深拷贝和浅拷贝两种情况:
a.深拷贝
let a = [0, 1, 2, 3] let b = [...a] console.log(a, b)
运行结果为:
a.push(4)
console.log(a, b)
运行结果为:
说明当数组是一维数组时,扩展运算符可以进行完全深拷贝,改变拷贝后数组的值并不会影响拷贝源的值。
b.浅拷贝
let a = [1, 2, 3, 4, 5, 6, [1, 2, 3]]; let b = [...a]; b.push(7); console.log(a,b); b[b.length - 2][0] = 100; console.log(a,b);
运行结果为:
从第一行结果可以看出,对新数组做一维操作时结果为深拷贝,而当改变新数组中第二层数组的值时,原数组第二层数组的值也跟着改变了,说明是浅拷贝。
结论:用扩展运算符对数组或者对象进行拷贝时,只能扩展和深拷贝第一层的值,对于第二层极其以后的值,扩展运算符将不能对其进行打散扩展,也不能对其进行深拷贝,即拷贝后和拷贝前第二层中的对象或者数组仍然引用的是同一个地址,其中一方改变,另一方也跟着改变。
②for()循环(浅拷贝)
numbers = [1, 2, 3]; numbersCopy = []; for (i = 0; i < numbers.length; i++) { numbersCopy[i] = numbers[i]; }
这个方法同样的只能用于一维数组: numbers[0] = 0; console.log(numbers, numbersCopy);
运行结果为:,说明是对于一维数组是深拷贝。
下面我们来看看多维数组的情况:
nestedNumbers = [[1], [2]]; numbersCopy = []; for (i = 0; i < nestedNumbers.length; i++) { numbersCopy[i] = nestedNumbers[i]; } numbersCopy[0].push(300); console.log(nestedNumbers, numbersCopy);
运行结果为:,因为我们使用的是“=”
运算符,它在处理数组/对象值的拷贝时通过引用而不是值复制,由于是公用引用,所以两个数组都被修改了。
③while()循环(浅拷贝)
【与for循环类似】
numbers = [1, 2, 3]; numbersCopy = []; i = -1; while (++i < numbers.length) { numbersCopy[i] = numbers[i]; }
④Array.map(浅拷贝)
map源于数学,是将一个集合转换成另一种集合,同时保留结构的概念。当我们使用map
方法时,需要给出一个callback
函数用于处理当前的数组,并返回一个新的数组元素。
numbers = [1, 2, 3]; numbersCopy = numbers.map((x) => x);//[1,2,3]
同样的,处理对象和数组的时候是引用而不是值复制。
⑤Array.filter(浅拷贝)
filter()是一种过滤方法,利用Array.filter()方法可以返回一个新数组,但是并不一定是返回同样长度的,这和我们的过滤条件有关。
numbers = [1, 2, 3]; numbersCopy = numbers.filter(() => true);// [1, 2, 3]
同样的,处理对象和数组的时候是引用而不是值复制。
⑥Array.reduce(浅拷贝)
reduce()
方法对数组中的每个元素执行一个由您提供的reducer
函数,将其结果汇总为单个返回值。
numbers = [1, 2, 3]; numbersCopy = numbers.reduce((newArray, element) => { newArray.push(element); return newArray; }, []); console.log(numbers,numbersCopy);//[1,2,3] [1,2,3]
上面我们的例子中初始值是一个空数组,我们在遍历原数组的时候来填充这个空数组。该数组必须要从下一个迭代函数的执行后被返回出来。
同样的,处理对象和数组的时候是引用而不是值复制。
⑦Array.slice(浅拷贝)
slice
方法根据我们指定的start、end的index从原数组中返回一个浅拷贝的数组。
[1, 2, 3, 4, 5].slice(0, 3);// [1, 2, 3] // 当不给定参数时,就返回原数组的拷贝 numbers = [1, 2, 3, 4, 5]; numbersCopy = numbers.slice();// [1, 2, 3, 4, 5]
同样的,处理对象和数组的时候是引用而不是值复制。
⑧Array.cancat(浅拷贝)
concat
将数组与值或其他数组进行组合。
[1, 2, 3].concat(4); // [1, 2, 3, 4] [1, 2, 3].concat([4, 5]); // [1, 2, 3, 4, 5] [1, 2, 3].concat(); // [1, 2, 3] [1, 2, 3].concat([]); // [1, 2, 3]
同样的,处理对象和数组的时候是引用而不是值复制。
⑨Array.from(浅拷贝)
可以将任何可迭代对象转换为数组。给一个数组返回一个浅拷贝。
console.log(Array.from('foo'))// ['f', 'o', 'o'] numbers = [1, 2, 3]; numbersCopy = Array.from(numbers)// [1, 2, 3]
同样的,处理对象和数组的时候是引用而不是值复制。
⑩JSON.parse & JSON.stringify(深拷贝)
JSON.stringify
将一个对象转成字符串;JSON.parse
将转成的字符串转回对象。
将它们组合起来可以将对象转换成字符串,然后反转这个过程来创建一个全新的数据结构。
nestedNumbers = [[1], [2]]; numbersCopyTemp = JSON.stringify(nestedNumbers); numbersCopy = JSON.parse(numbersCopyTemp); numbersCopy[0].push(300); console.log(nestedNumbers,numbersCopyTemp,numbersCopy);//[ [ 1 ], [ 2 ] ] '[[1],[2]]' [ [ 1, 300 ], [ 2 ] ]
这个可以安全地拷贝深度嵌套的对象/数组。
有几种特殊情况:
a.如果obj里面有时间对象,则JSON.stringify后再JSON.parse的结果,时间将只是字符串的形式。而不是时间对象。
var test = { name: 'a', date: [new Date(), new Date()], }; let b = JSON.parse(JSON.stringify(test)); let c = new Date(); console.log(b, c);//{ name: 'a',date: [ '2019-10-24T13:14:31.841Z', '2019-10-24T13:14:31.841Z' ] } 2019-10-24T13:14:31.841Z
b.如果obj里有RegExp、Error对象,则序列化的结果将只得到空对象。
const test = { name: 'a', date: new RegExp('\w+'), }; const copyed = JSON.parse(JSON.stringify(test)); test.name = 'test'; console.log('ddd', test, copyed); //ddd { name: 'test', date: /w+/ } { name: 'a', date: {} }
c.如果obj里有函数,undefined,则序列化的结果会把函数或 undefined丢失。
const test = { name: 'a', date: function hehe() { console.log('fff') }, }; const copyed = JSON.parse(JSON.stringify(test)); test.name = 'test' console.log('ddd', test, copyed); //ddd { name: 'test', date: [Function: hehe] } { name: 'a' }
d.如果obj里有NaN、Infinity和-Infinity,则序列化的结果会变成null
e.JSON.stringify()只能序列化对象的可枚举的自有属性,如果obj中的对象是有构造函数生成的, 则使用JSON.parse(JSON.stringify(obj))深拷贝后,会丢弃对象的constructor。
function Person(name) { this.name = name; console.log(name) } const Alice = new Person('Alice'); //Alice const test = { name: 'a', date: Alice, }; const copyed = JSON.parse(JSON.stringify(test)); test.name = 'test' console.log('ddd', test, copyed) //ddd { name: 'test', date: Person { name: 'Alice' } } { name: 'a', date: { name: 'Alice' } }
摘自:https://www.cnblogs.com/ld1024/p/10750162.html