原生js的對象拷貝都是淺拷貝,若是須要深拷貝則要手動作一些操做json
例子:數組
let obj = { name: 'lizitao', msg: { age: '2' } }; let cp = JSON.parse(JSON.stringify(obj)); console.log(cp);
優勢: 寫起來很是簡單
缺點:侷限性太多了。由於只支持json所支持的那些數據類型。像函數、undefined、正則等這些都是不支持的。遇到環狀結構則會報錯:app
var obj = { a: undefined }; console.log(JSON.parse(JSON.stringify(obj))) // {} // 屬性a被自動忽略了
JSON.stringify則會檢測是否存在環,存在則拋出錯誤函數
var obj = {}; obj.child = obj; JSON.stringify(a) // Uncaught TypeError: Converting circular structure to JSON
因此咱們只能複雜一點的方式,用遞歸來實現深拷貝this
const clone = function(source){ if(source instanceof Object){ let result = {} for (const key in source) { result[key] = clone(source[key]) } return result } else { return source } }
先判斷是否爲對象,非對象則直接返回它的值。若是是對象則新建一個空對象 {},並複製屬性到該對象上。注意屬性的複製是遞歸調用clone函數的:result[key] = clone(source[key])
,這樣才能保證每一層都是深拷貝!。
不過它只判斷了是否爲對象,並假設對象都是像{name: value}的形式。若是對象是數組、函數、日期等等就不能正常運行了。因此要判斷對象究竟是哪一種類型的對象,並根據對象具體類型來初始化result,用這樣的形式:code
if(source instanceof Object){ //... if(source instanceof Array){ //... } else if(source instanceof Function){ //... } else if(source instanceof Date){ //... } // 省略許多else if, 判斷越詳細那麼支持的對象類型就越多 else { //... } } else { //... }
因而咱們按照這個思路寫出第二版對象
const clone = function(source){ if(source instanceof Object){ let result = null if(source instanceof Array){ result = [] } else if(source instanceof Function){ result = function(){ return source.apply(this, arguments) } } else if (source instanceof Date) { result = new Date(source); } else { result = {} } for (const key in source) { result[key] = this.clone(source[key]) } return result } else { return source } }
最後要解決的是對象是環的狀況。基本思路是用一個數組來存儲全部複製過的對象,每次調用前先判斷對象是否複製過了,若是複製過就直接返回。遞歸