第十四天挑戰(數據的復制)
地址:https://javascript30.com/
所有內容均上傳至gitee,答案不唯一,僅代表本人思路
中文詳解:https://github.com/soyaine/JavaScript30
該詳解是Soyaine及其團隊整理編撰的,是對源代碼的詳解,強烈推薦大家觀看學習!!!
本人gitee:https://gitee.com/thats-all-right-ha-ha/30-days—js-challenge
官方代碼
本期內容是關于基本數據類型和引用數據類型在復制時的特性和解決方法
基礎類型
//start with strings, numbers and booleans
let age = 100;
let age2 = age;
console.log(age, age2);
age = 200;
console.log(age, age2);let name = 'Wes';
let name2 = name;
console.log(name, name2);
name = 'wesley';
console.log(name, name2);
分析
- 基礎數據類型:
- 布爾值(Boolean),有 2 個值分別是:
true
和false
。 - null,一個表明 null 值的特殊關鍵字。JavaScript 是大小寫敏感的,因此
null
與Null
、NULL
或變體完全不同。 - undefined,和 null 一樣是一個特殊的關鍵字,undefined 表示變量未賦值時的屬性。
- 數字(Number),整數或浮點數,例如:
42
或者3.14159
。 - 任意精度的整數(BigInt),可以安全地存儲和操作大整數,甚至可以超過數字的安全整數限制。
- 字符串(String),字符串是一串表示文本值的字符序列,例如:
"Howdy"
。 - 代表(Symbol,在 ECMAScript 6 中新添加的類型)。一種實例是唯一且不可改變的數據類型。
- 布爾值(Boolean),有 2 個值分別是:
- 基礎數據類型的值均存儲在棧中,每個值都有一個獨立的內存空間,在a變量復制給b變量的時候復制的是一個具體的值,在a變量進行更改的時候,不會影響到b變量的值
引用類型(數組)
// Let's say we have an array
const players = ['Wes', 'Sarah', 'Ryan', 'Poppy'];// and we want to make a copy of it.
const team = players;console.log(players, team);
// You might think we can just do something like this:
// team[3] = 'Lux';// however what happens when we update that array?// now here is the problem!// oh no - we have edited the original array too!// Why? It's because that is an array reference, not an array copy. They both point to the same array!// So, how do we fix this? We take a copy instead!
const team2 = players.slice();// one way// or create a new array and concat the old one in
const team3 = [].concat(players);// or use the new ES6 Spread
const team4 = [...players];
team4[3] = 'heeee hawww';
console.log(team4);const team5 = Array.from(players);// now when we update it, the original one isn't changed
分析
- 引用類型(數組)
- 一般情況下引用類型的值會作為數據的有序或無序集合即一個數組中包含著許多的數據,通常他們的數據體量都較為龐大,所以一般引用類型的數據都存放在堆內存中,并且在棧內存中開辟一個地址單元指向其堆內存中的地址
- 在數組進行復制的時候,本質上復制的是數組在堆內存中的地址,也就是 team和players這兩個變量指向的是同一個數組,那么當其中一個對其進行改變的時候,另一個也會受到一定的影響
- 那么如何解決這個問題?
- slice方法:slice方法會返回一個新的數組,這個數組的值是原數組的淺拷貝,slice的返回的數組和原數組是兩個數組,不會造成影響
- concat:方法用于合并兩個或多個數組。此方法不會更改現有數組,而是返回一個新數組
- 展開運算符
- 創建新的數組實例
引用類型(對象)
// with Objects
const person = {
name: 'Wes Bos',
age: 80
};// and think we make a copy:
// const captain = person;
// captain.number = 99;// how do we take a copy instead?
const cap2 = Object.assign({}, person, { number: 99, age: 12 });
console.log(cap2);// We will hopefully soon see the object ...spread
// const cap3 = {...person};// Things to note - this is only 1 level deep - both for Arrays and Objects. lodash has a cloneDeep method, but you should think twice before using it.const wes = {
name: 'Wes',
age: 100,
social: {
twitter: '@wesbos',
facebook: 'wesbos.developer'
}
};console.clear();
console.log(wes);const dev = Object.assign({}, wes);const dev2 = JSON.parse(JSON.stringify(wes));
分析
- 對象也是引用類型,和數組的存儲原理基本一致,這里直接上解決方法
- 解決方法
- Object.assign():將一個或者多個源對象中所有可枚舉的自有屬性復制到目標對象,并返回修改后的目標對象。
- JSON.parse & JSON.stringify:后者將對象轉換成字符串,前者將字符串再復原成對象
擴展知識
引用類型的深拷貝
數組和對象的深拷貝在JavaScript中是一個常見需求,尤其是在處理復雜數據結構時,以確保原始數據不被無意修改。深拷貝意味著復制對象及其嵌套對象的實際值,而不僅僅是復制引用地址。以下是實現深拷貝的幾種方法:
1. 使用 JSON.stringify()
和 JSON.parse()
這種方法簡單易用,但是有其局限性,比如不能復制函數、undefined、Symbol、循環引用等。
javascriptCopy codeconst obj = {a: 1,b: {c: 2,d: 3,},
};const arr = [1, [2, 3], { a: 4 }];const deepCopiedObj = JSON.parse(JSON.stringify(obj));
const deepCopiedArr = JSON.parse(JSON.stringify(arr));// 修改深拷貝后的對象和數組,不會影響原始數據
deepCopiedObj.b.c = 20;
deepCopiedArr[1][0] = 200;console.log(obj.b.c); // 輸出 2
console.log(arr[1][0]); // 輸出 2
2. 使用遞歸
遞歸方法可以更靈活地處理各種數據類型,包括數組和對象,但需要手動實現。
javascriptCopy codefunction deepCopy(value, hash = new WeakMap()) {if (typeof value !== 'object' || value === null) {return value;}if (hash.has(value)) {return hash.get(value);}let copy = Array.isArray(value) ? [] : {};hash.set(value, copy);for (let key in value) {if (value.hasOwnProperty(key)) {copy[key] = deepCopy(value[key], hash);}}return copy;
}const obj = { a: 1, b: { c: 2 } };
const arr = [1, [2, 3], { a: 4 }];const deepCopiedObj = deepCopy(obj);
const deepCopiedArr = deepCopy(arr);deepCopiedObj.b.c = 20;
deepCopiedArr[1][0] = 200;console.log(obj.b.c); // 輸出 2
console.log(arr[1][0]); // 輸出 2
3. 使用 structuredClone()
從 ES2021 開始,structuredClone()
方法提供了一種官方、高效的深拷貝解決方案,支持大多數數據類型,包括循環引用,但不支持復制函數。
javascriptCopy codeconst obj = { a: 1, b: { c: 2 } };
const arr = [1, [2, 3], { a: 4 }];const deepCopiedObj = structuredClone(obj);
const deepCopiedArr = structuredClone(arr);deepCopiedObj.b.c = 20;
deepCopiedArr[1][0] = 200;console.log(obj.b.c); // 輸出 2
console.log(arr[1][0]); // 輸出 2
structuredClone()
是目前最推薦的深拷貝實現方式,因為它既能處理復雜數據結構,包括循環引用,又能通過瀏覽器和Node.js環境的標準API直接使用。不過,它可能不適用于舊版瀏覽器或某些特殊環境,需要根據實際情況選擇合適的方法。