首先咱們要實現一個getType函數對元素進行類型判斷,直接調用Object.prototype.toString
方法。json
function getType(obj){ //tostring會返回對應不一樣的標籤的構造函數 var toString = Object.prototype.toString; var map = { '[object Boolean]' : 'boolean', '[object Number]' : 'number', '[object String]' : 'string', '[object Function]' : 'function', '[object Array]' : 'array', '[object Date]' : 'date', '[object RegExp]' : 'regExp', '[object Undefined]': 'undefined', '[object Null]' : 'null', '[object Object]' : 'object' }; if(obj instanceof Element) { return 'element'; } return map[toString.call(obj)]; }
對於一個引用類型,若是直接將它賦值給另外一個變量,因爲這兩個引用指向同一個地址,這時改變其中任何一個引用,另外一個都會受到影響。當咱們想複製一個對象而且切斷與這個對象的聯繫,就要使用深拷貝。對於一個對象來講,因爲可能有多層結構,因此咱們可使用遞歸來解決這個問題數組
function deepClone(data){ var type = getType(data); var obj; if(type === 'array'){ obj = []; } else if(type === 'object'){ obj = {}; } else { //再也不具備下一層次 return data; } if(type === 'array'){ for(var i = 0, len = data.length; i < len; i++){ obj.push(deepClone(data[i])); } } else if(type === 'object'){ for(var key in data){ obj[key] = deepClone(data[key]); } } return obj; }
對於function類型,這裏是直接賦值的,仍是共享一個內存值。這是由於函數更多的是完成某些功能,有個輸入值和返回值,並且對於上層業務而言更多的是完成業務功能,並不須要真正將函數深拷貝。函數
上面是使用遞歸來進行深拷貝,顯然咱們可使用樹的廣度優先遍從來實現spa
//這裏爲了閱讀方便,只深拷貝對象,關於數組的判斷參照上面的例子 function deepClone(data){ var obj = {}; var originQueue = [data]; var copyQueue = [obj]; //如下兩個隊列用來保存複製過程當中訪問過的對象,以此來避免對象環的問題(對象的某個屬性值是對象自己) var visitQueue = []; var copyVisitQueue = []; while(originQueue.length > 0){ var _data = originQueue.shift(); var _obj = copyQueue.shift(); visitQueue.push(_data); copyVisitQueue.push(_obj); for(var key in _data){ var _value = _data[key] if(typeof _value !== 'object'){ _obj[key] = _value; } else { //使用indexOf能夠發現數組中是否存在相同的對象(實現indexOf的難點就在於對象比較) var index = visitQueue.indexOf(_value); if(index >= 0){ // 出現環的狀況不須要再取出遍歷 _obj[key] = copyVisitQueue[index]; } else { originQueue.push(_value); _obj[key] = {}; copyQueue.push(_obj[key]); } } } } return obj; }
深拷貝對象還有另外一個解決方法,在對象中不含有函數的時候,使用JSON解析反解析就能夠獲得一個深拷貝對象prototype