關於js基礎知識的一些整理 陣列拷貝
一.陣列淺拷貝(2020年8月10號)
方法一:
Array.prototype.slice() Array.prototype.concat()
這兩種方法都可以返回一個新的陣列
let user = [{name: "leo"}, {age: 18}]; let user1 = [{age: 20},{addr: "fujian"}]; let user2 = user.concat(user1); user1[0]["age"] = 25; console.log(user); // [{"name":"leo"},{"age":18}] console.log(user1); //View Code[{"age":25},{"addr":"fujian"}] console.log(user2); // [{"name":"leo"},{"age":18},{"age":25},{"addr":"fujian"}]
注意:但是對於屬性的值是物件的話只會拷貝一份相同的記憶體地址
方法二:拓展運算子(...)
語法:var cloneObj = { ...obj };
擴充套件運算子也是淺拷貝,對於值是物件的屬性無法完全拷貝成2個不同物件,但是如果屬性都是基本型別的值的話,使用擴充套件運算子也是優勢方便的地方
let user = { name: "leo", skill: { JavaScript: 90, CSS: 80}}; let leo= {...user}; leo.name = "leo1"; leo.skill.CSS = 90; console.log(leo.name); // "leo1" ⚠️ 差異! console.log(user.name); // "leo" ⚠️ 差異! console.log(leo.skill.CSS); // 90 console.log(user.skill.CSS);// 90
上面注意差異存在的地方
手寫淺拷貝
實現原理:新的物件複製已有物件中非物件屬性的值和物件屬性的「引用」,也就是說物件屬性並不複製到記憶體。
function cloneShallow(source) { let target= {}; for (let key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } return target; }
- 「for in」
for...in語句以任意順序遍歷一個物件自有的、繼承的、可列舉的
、非Symbol的屬性。對於每個不同的屬性,語句都會被執行。
- 「hasOwnProperty」
該函式返回值為布林值,所有繼承了 Object 的物件都會繼承到hasOwnProperty
方法,和in
運算子不同,該函式會忽略掉那些從原型鏈上繼承到的屬性和自身屬性。語法:obj.hasOwnProperty(prop)
prop
是要檢測的屬性「字串名稱」或者Symbol
。
二 :深拷貝
1. JSON.parse(JSON.stringify())
其原理是把一個物件序列化成為一個JSON字串,將物件的內容轉換成字串的形式再儲存在磁碟上,再用JSON.parse()
反序列化將JSON字串變成一個新的物件。
但是使用 JSON.stringify()
使用注意:
- 拷貝的物件的值中如果有函式,
undefined
,symbol
則經過JSON.stringify()
`序列化後的JSON字串中這個鍵值對會消失; - 無法拷貝不可列舉的屬性,無法拷貝物件的原型鏈;
- 拷貝
Date
引用型別會變成字串; - 拷貝
RegExp
引用型別會變成空物件; - 物件中含有
NaN
、Infinity
和-Infinity
,則序列化的結果會變成null
; - 無法拷貝物件的迴圈應用(即
obj[key] = obj
)。
2.手寫深拷貝
核心思想是「遞迴」,遍歷物件、陣列直到裡邊都是基本資料型別,然後再去複製,就是深度拷貝。實現程式碼:
const isObject = obj => typeof obj === 'object' && obj != null; function cloneDeep(source) { if (!isObject(source)) return source; // 非物件返回自身 const target = Array.isArray(source) ? [] : {}; for(var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { if (isObject(source[key])) { target[key] = cloneDeep(source[key]); // 注意這裡 } else { target[key] = source[key]; } } } return target; }
該方法缺陷:遇到迴圈引用,會陷入一個迴圈的遞迴過程,從而導致爆棧。其他寫法,可以閱讀《如何寫出一個驚豔面試官的深拷貝?》 。
總結:
「淺拷貝」:將物件的每個屬性進行依次複製,但是當物件的屬性值是引用型別時,實質複製的是其引用,當引用指向的值改變時也會跟著變化。
「深拷貝」:複製變數值,對於引用資料,則遞迴至基本型別後,再複製。深拷貝後的物件「與原來的物件完全隔離」,互不影響,對一個物件的修改並不會影響另一個物件。
「深拷貝和淺拷貝是針對複雜資料型別來說的,淺拷貝只拷貝一層,而深拷貝是層層拷貝。」
更多詳細內容 檢視文章:前端自測清單