1. 程式人生 > 實用技巧 >關於js基礎知識的一些整理 陣列拷貝

關於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); //
[{"age":25},{"addr":"fujian"}] console.log(user2); // [{"name":"leo"},{"age":18},{"age":25},{"addr":"fujian"}]
View Code

注意:但是對於屬性的值是物件的話只會拷貝一份相同的記憶體地址

方法二:拓展運算子(...)

語法: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()使用注意:

  • 拷貝的物件的值中如果有函式,undefinedsymbol則經過JSON.stringify()`序列化後的JSON字串中這個鍵值對會消失;
  • 無法拷貝不可列舉的屬性,無法拷貝物件的原型鏈;
  • 拷貝Date引用型別會變成字串;
  • 拷貝RegExp引用型別會變成空物件;
  • 物件中含有NaNInfinity-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;
}

  該方法缺陷:遇到迴圈引用,會陷入一個迴圈的遞迴過程,從而導致爆棧。其他寫法,可以閱讀《如何寫出一個驚豔面試官的深拷貝?》 。

總結:

「淺拷貝」:將物件的每個屬性進行依次複製,但是當物件的屬性值是引用型別時,實質複製的是其引用,當引用指向的值改變時也會跟著變化。

「深拷貝」:複製變數值,對於引用資料,則遞迴至基本型別後,再複製。深拷貝後的物件「與原來的物件完全隔離」,互不影響,對一個物件的修改並不會影響另一個物件。

「深拷貝和淺拷貝是針對複雜資料型別來說的,淺拷貝只拷貝一層,而深拷貝是層層拷貝。」

更多詳細內容 檢視文章:前端自測清單