1.簡單版:
<script type="text/javascript"> const newObj = JSON.parse(JSON.stringify(oldObj)); </script>
局限性:
他無法實現對函數 、RegExp等特殊對象的克隆
會拋棄對象的constructor,所有的構造函數會指向Object
對象有循環引用,會報錯
2.面試版:
<script type="text/javascript">
/**
* deep clone
* @param {[type]} parent object 需要進行克隆的對象
* @return {[type]} 深克隆后的對象
*/
const clone = parent => {
// 判斷類型
const isType = (obj, type) => {
if (typeof obj !== "object") return false;
const typeString = Object.prototype.toString.call(obj);
let flag;
switch (type) {
case "Array":
flag = typeString === "[object Array]";
break;
case "Date":
flag = typeString === "[object Date]";
break;
case "RegExp":
flag = typeString === "[object RegExp]";
break;
default:
flag = false;
}
return flag;
};
// 處理正則
const getRegExp = re => {
var flags = "";
if (re.global) flags += "g";
if (re.ignoreCase) flags += "i";
if (re.multiline) flags += "m";
return flags;
};
// 維護兩個儲存循環引用的數組
const parents = [];
const children = [];
const _clone = parent => {
if (parent === null) return null;
if (typeof parent !== "object") return parent;
let child, proto;
if (isType(parent, "Array")) {
// 對數組做特殊處理
child = [];
} else if (isType(parent, "RegExp")) {
// 對正則對象做特殊處理
child = new RegExp(parent.source, getRegExp(parent));
if (parent.lastIndex) child.lastIndex = parent.lastIndex;
} else if (isType(parent, "Date")) {
// 對Date對象做特殊處理
child = new Date(parent.getTime());
} else {
// 處理對象原型
proto = Object.getPrototypeOf(parent);
// 利用Object.create切斷原型鏈
child = Object.create(proto);
}
// 處理循環引用
const index = parents.indexOf(parent);
if (index != -1) {
// 如果父數組存在本對象,說明之前已經被引用過,直接返回此對象
return children[index];
}
parents.push(parent);
children.push(child);
for (let i in parent) {
// 遞歸
child[i] = _clone(parent[i]);
}
return child;
};
return _clone(parent);
};
</script>
局限性:
一些特殊情況沒有處理: 例如Buffer對象、Promise、Set、Map
另外對於確保沒有循環引用的對象,我們可以省去對循環引用的特殊處理,因為這很消耗時間
