常常遇到数组或对象等引用类型做为函数的参数的状况,但又不想修改原来的数据,这时候就须要拷贝(基本类型的变量不须要考虑)。
拷贝分为浅拷贝和深拷贝。浅拷贝是引用复制,深拷贝是彻底单纯拷贝数据的值。由于数组是最多见的引用类型,因此下面大部分拿数组举例。git
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也被修改了。这显然不是想要的作法。es6
要想不改变原来的数组,就要用到如下的几种方法:github
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]
还能够部分拷贝json
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中的值,放到空数组中函数
可是,以上这几种拷贝方法看似都不会改变原来数组,其实也仍是属于浅拷贝范畴。若是原数组里面还有引用类型数组,这些方法都会失效(好比二维数组)code
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)ip
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; }