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
另外對於確保沒有循環引用的對象,我們可以省去對循環引用的特殊處理,因為這很消耗時間