常常遇到數組或對象等引用類型做爲函數的參數的狀況,但又不想修改原來的數據,這時候就須要拷貝(基本類型的變量不須要考慮)。
拷貝分爲淺拷貝和深拷貝。淺拷貝是引用複製,深拷貝是徹底單純拷貝數據的值。由於數組是最多見的引用類型,因此下面大部分拿數組舉例。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; }