经常遇到数组或对象等引用类型作为函数的参数的情况,但又不想修改原来的数据,这时候就需要拷贝(基本类型的变量不需要考虑)。
拷贝分为浅拷贝和深拷贝。浅拷贝是引用复制,深拷贝是完全单纯拷贝数据的值。因为数组是最常见的引用类型,所以下面大部分拿数组举例。
浅拷贝
- 简单把引用复制
var a = [1,2,3]
var b = a
console.log(b) //Array(3) [1, 2, 3]
b[1] = 0
console.log(b) //Array(3) [1, 0, 3]
console.log(a) //Array(3) [1, 0, 3]
可以看出,修改数组b的时候,数组a也被修改了。这显然不是想要的做法。
要想不改变原来的数组,就要用到以下的几种方法:
- 循环 for(...)
- concat方法
var a = [1,2,3]
var b = a.concat() //或 var b = [].concat(a)
console.log(b) //Array(3) [1, 2, 3]
b[1] = 0
console.log(b) //Array(3) [1, 0, 3]
console.log(a) //Array(3) [1, 2, 3]
var a = [1,2,3,4,5]
var b = a.slice(0)
console.log(b) //Array(5) [1, 2, 3, 4, 5]
b[0] = 0
console.log(a) //Array(5) [1, 2, 3, 4, 5]
console.log(b) //Array(5) [0, 2, 3, 4, 5]
还可以部分拷贝
var a = [1,2,3,4,5]
var b = a.slice(1, 3) //返回[1,3)下标区间的数
console.log(b) //Array(2) [2, 3]
//负数也可以
console.log(a.slice(-2)) //Array(2) [4, 5]
还有一种方法,跟循环的方法有点类似,就是es6的新特性,展开语法:
var a = [1,2,3,4,5]
var b = [...a]
b[0] = 0
console.log(a) //Array(5) [1, 2, 3, 4, 5]
这个方法就是逐一枚举a中的值,放到空数组中
但是,以上这几种拷贝方法看似都不会改变原来数组,其实也还是属于浅拷贝范畴。如果原数组里面还有引用类型数组,这些方法都会失效(比如二维数组)
var a = [[1,2,3],[4,5,6]]
var b = a.slice(0)
b[0][4] = 0
console.log(a[0]) //[[1, 2, 0],[4,5,6]]
其他concat,[...]等方法也一样。
可以这么理解:原数组就像一个带锁的独一无二的箱子,里面有各种零食。简单的引用复制,其实就是配了一把钥匙,谁动过里面的东西,其他人都会受到影响。而上面这4种方法其实就是自己买一个不同箱子,参照原来的箱子里面的零食,去某宝买同款。动自己箱子的东西,原来的箱子不受影响。但是如果原来的箱子里面还套了个独一无二带锁的箱子,某宝买不到同款,那没办法,里面的箱子只能还是配把钥匙共用。所以,这4种方法只是简单绕过第一层箱子的引用复制
深拷贝
目前比较好的方法就是json大法JSON.stringify(),要么就是自己写递归的深拷贝函数。
JSON.stringify()是将对象或数组序列化成字符串。然后再用JSON.parse()解析成值或对象。
var a = {aa:1,bb:[1,2],cc:[3,4]}
var b = JSON.parse(JSON.stringify(a))
console.log(b) //{aa:1,bb:[1,2],cc:[3,4]}
b.cc[0] = 0
console.log(a) //{aa:1,bb:[1,2],cc:[3,4]}
console.log(b) //{aa:1,bb:[1,2],cc:[0,4]}
附带深拷贝的自定义函数(源自大佬mqyqingfeng的github)
var deepCopy = function(obj) {
if (typeof obj !== 'object') return;
var newObj = obj instanceof Array ? [] : {};
for (var key in obj) {
if (obj.hasOwnProperty(key)) {
newObj[key] = typeof obj[key] === 'object' ? deepCopy(obj[key]) : obj[key];
}
}
return newObj;
}
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。