559 物件和陣列的深淺拷貝
阿新 • • 發佈:2020-10-23
陣列淺拷貝練習題
let utils = (function () { /* * toArray:轉換為陣列的方法 * @params * 不固定數量,不固定型別 * @return * [Array] 返回的處理後的新陣列 * by haha on 2020 */ // 方法1 function toArray(...args) { // ES6的剩餘運算子獲取的引數集合本身就是陣列 return args; } // 方法2 function toArray() { // arguments是實參集合,獲取的結果是一個類陣列(箭頭函式中沒有arguments),不能直接調取陣列的辦法(因為它不是ARRAY的例項) // return [...arguments]; return Array.from(arguments); } // 方法3 function toArray() { // 把類陣列轉換為陣列(克隆) // 原理:只要把slice執行,讓方法中的this變為arguments,這樣就可以實現把類陣列arguments轉換為陣列 // 1.讓slice執行: Array.prototype.slice() 或者 [].slice() // 2.把this改變成為arguments [].slice.call(arguments) // 前提:運算元組的程式碼(內建方法中的程式碼)也需要適配改變的THIS(類陣列) [].forEach.call(arguments, item => { console.log(item); }); return [].slice.call(arguments); /* let arr = []; for (let i = 0; i < arguments.length; i++) { arr.push(arguments[i]); } return arr; */ } return { toArray }; })(); let ary = utils.toArray(10, 20, 30); // => [10,20,30] console.log(ary); ary = utils.toArray('A', 10, 20, 30); // => ['A',10,20,30] console.log(ary); // 簡單模擬slice的實現 Array.prototype.slice = function slice() { // this -> ary let arr = []; for (let i = 0; i < this.length; i++) { arr.push(this[i]); } return arr; }; // let ary = [10, 20, 30, 40]; // ary.slice() 把陣列克隆一份 // console.log(ary.slice());
淺拷貝
- 克隆:記憶體地址是不一樣的
【for迴圈也可以實現淺拷貝。】
實現物件的克隆:
Object.assign:淺比較/淺克隆
{...obj}:展開運算子,也只能展開第一級,也是淺克隆
newObj = obj :這不是克隆,只是賦值
let obj1 = { a: 1, b: 2, c: [5, 6] } let obj2 = Object.assign({}, obj1) console.log(obj2, obj1 == obj2) // {a: 1, b: 2, c: Array(2)} false console.log(obj1.c === obj2.c) // true let obj3 = { ...obj1 } console.log(obj3) // {a: 1, b: 2, c: Array(2)} console.log(obj1.c === obj3.c) // true
實現陣列的克隆
slice、concat... 這些都是淺克隆
forEach、map、reduce... 遍歷也只是遍歷第一級
let newArr = arr.concat([]);
console.log(newArr === arr); // => false
console.log(newArr[2] === arr[2]); // => true
// 補充:使用for迴圈實現物件、陣列的淺拷貝 // 使用for迴圈實現物件的淺拷貝 let obj1 = { a: 1, b: 2, c: [5, 6] } let newObj = {} for (let i in obj1) { newObj[i] = obj1[i] } console.log(newObj) // {a: 1, b: 2, c: Array(2)} console.log(obj1.c === newObj.c) // true // --------------------------- // 使用for迴圈實現陣列的淺拷貝 let arr = [11, 22, {name: '哈哈'}] let newArr = [] for(let i=0;i<arr.length;i++) { newArr[i] = arr[i] } console.log(newArr) // [11, 22, {name: '哈哈'}] console.log(newArr[2] === arr[2]) // true
深拷貝
JSON.parse(JSON.stringify())
// 暴力法:把原始資料直接變為字串,再把字串變為物件(此時瀏覽器要重新開闢所有的記憶體空間),實現出深度克隆、深拷貝
let arr = [11, 22, { name: '哈哈' }]
let newArr = JSON.parse(JSON.stringify(arr));
console.log(newArr);
console.log(newArr === arr); // => false
console.log(newArr[2] === arr[2]); // => false
// 出現的問題:正則會變為空物件,日期變為字串,函式、undefined、Symbol直接消失,BigInt直接報錯
let newObj = JSON.parse(JSON.stringify(obj));
console.log(newObj);
console.log(newObj === obj); // => false
console.log(newObj.c === obj.c); // => false
自己實現深拷貝
/*
補充:獲取例項的建構函式
console.log((11).constructor) // Number函式
console.log(('11').constructor) // String函式
console.log((true).constructor) // Boolean函式
console.log((false).constructor) // Boolean函式
console.log((BigInt(10)).constructor) // BigInt函式
console.log((BigInt('33')).constructor) // BigInt函式
// console.log((undefined).constructor) // 報錯
// console.log((null).constructor) // 報錯
console.log((function fn() {}).constructor) // Function函式
console.log(({}).constructor) // Object函式
*/
/* 陣列或者物件的深克隆/淺克隆 */
let obj = {
a: 100,
b: [10, 20, 30],
c: function () { },
d: /^\d+$/,
e: new Date(),
f: Symbol('f'),
g: BigInt('10')
};
let arr = [10, [100, 200], {
x: 10,
y: 20
}];
// 深克隆、深拷貝
function cloneDeep(obj) {
// 獲取例項的建構函式 【當obj是部分基本資料型別,或者是空物件{}的時候,這樣寫有問題,應該先用Object(obj)轉換】
const constructor = obj.constructor;
if (obj === null) return null;
if (typeof obj !== "object") return obj;
// 正則例項、日期例項的constructor都是返回RegExp、Date建構函式 --> console.log(new Date().constructor):Date、 console.log(/\d/.constructor):RegExp.
// constructor.name:返回建構函式的名字:(11).constructor.name --> Number,是constructor.name,不是constructor
if (/^(RegExp|Date)$/i.test(constructor.name)) return new constructor(obj);
// 重新建立一個constructor類的例項,用於儲存克隆的資料
// 不能寫成 let clone = {},因為這樣寫就是往物件裡克隆,而有時候克隆的是陣列,so應該往obj同樣的資料型別的例項中克隆
let clone = new constructor();
for (let key in obj) {
if (!obj.hasOwnProperty(key)) break;
// 如果不遞迴,只會實現淺拷貝。如果obj[key]是引用型別,需要重新cloneDeep。不管是基本、引用型別,都cloneDeep。
clone[key] = cloneDeep(obj[key]);
}
return clone;
}
let newArr = cloneDeep(arr);
console.log(newArr);
console.log(newArr === arr); // => false
console.log(newArr[2] === arr[2]); // => false
let newObj = cloneDeep(obj);
console.log(newObj);
console.log(newObj === obj); // => false
console.log(newObj.d === obj.d); // => false