慎用JS中的slice()、concat()和assign()方法來複制數組

1、原數組裏的數據不包含引用類型

let arr1 = [1 , 2 , 3 , "hello" , "world"];  //原數組
複製代碼

一、使用 slice() 方法

拷貝數組:es6

let arr2 = arr1.slice(0);
console.log(arr2);   //打印新數組
[1 , 2 , 3 , "hello" , "world"];  //新數組
複製代碼

修改通過 slice() 拷貝過的新數組:數組

arr2[3] = "Hello";
console.log(arr1);  //打印舊數組
[1 , 2 , 3 , "hello" , "world"]   
console.log(arr2);   //打印新數組
[1 , 2 , 3 , "Hello" , "world"]
複製代碼

結論:使用 slice() 方法拷貝數組,而後修改新數組,不會影響到舊數組的值。瀏覽器

二、使用 concat() 方法

拷貝數組:bash

let arr3 = [].conat(arr1);
console.log(arr3);   //打印新數組
[1 , 2 , 3 , "hello" , "world"];  //新數組
複製代碼

修改通過 concat() 拷貝過的新數組ui

arr3[3] = "Hello";
console.log(arr1);  //打印舊數組
[1 , 2 , 3 , "hello" , "world"]   
console.log(arr3);   //打印新數組
[1 , 2 , 3 , "Hello" , "world"]
複製代碼

結論:使用 concat() 方法拷貝數組,而後修改新數組,不會影響到舊數組的值。spa

三、使用 assign() 方法,es6中的對象方法,注意瀏覽器的兼容性

拷貝數組:code

let arr4 = Object.assign({} , arr1);
console.log(arr4);   //打印新數組
[1 , 2 , 3 , "hello" , "world"];  //新數組
複製代碼

修改通過 assign() 拷貝過的新數組對象

arr4[3] = "Hello";
console.log(arr1);  //打印舊數組
[1 , 2 , 3 , "hello" , "world"]   
console.log(arr4);   //打印新數組
[1 , 2 , 3 , "Hello" , "world"]
複製代碼

結論:使用 assign() 方法拷貝數組,而後修改新數組,不會影響到舊數組的值。遞歸

四、使用簡單的數組賦值語法

拷貝數組:內存

let arr5 = arr1;
console.log(arr5);   //打印新數組
[1 , 2 , 3 , "hello" , "world"];  //新數組
複製代碼

修改通過簡單賦值過的新數組

arr5[3] = "Hello";
console.log(arr1);  //打印舊數組
[1 , 2 , 3 , "Hello" , "world"]   
console.log(arr5);   //打印新數組
[1 , 2 , 3 , "Hello" , "world"]
複製代碼

結論:使用數組簡單賦值方法拷貝數組,而後修改新數組,會影響到舊數組的值。

緣由:這種簡單賦值的方法屬於數組的淺拷貝,數組arr1和數組arr5共用同一塊內存,其中一個數組改變,另外一個數組也會跟着改變。

2、原數組裏的數據包含引用類型

let arr1 = [1 , 2 , 3 , {"name" : "張小二"} , {"sex" : "male"}];  //原數組
複製代碼

一、使用 slice() 方法

拷貝數組:

let arr2 = arr1.slice(0);
console.log(arr2);   //打印新數組
[1 , 2 , 3 , {"name" : "張小二"} , {"sex" : "male"}];  //新數組
複製代碼

修改通過 slice() 拷貝過的新數組:

arr2[3].name = "隔壁張小二";
console.log(arr1);  //打印舊數組
[1 , 2 , 3 , {"name" : "隔壁張小二"} , {"sex" : "male"}]   
console.log(arr2);   //打印新數組
[1 , 2 , 3 , {"name" : "隔壁張小二"} , {"sex" : "male"}]
複製代碼
結論:使用 slice() 方法拷貝數組,而後修改新數組,會改變舊數組的值。

二、使用 concat() 方法

拷貝數組:

let arr3 = [].conat(arr1);
console.log(arr3);   //打印新數組
[1 , 2 , 3 , {"name" : "張小二"} , {"sex" : "male"}];  //新數組
複製代碼

修改通過 concat() 拷貝過的新數組

arr3[3].name = "隔壁張小二";
console.log(arr1);  //打印舊數組
[1 , 2 , 3 , {"name" : "隔壁張小二"} , {"sex" : "male"}]   
console.log(arr3);   //打印新數組
[1 , 2 , 3 , {"name" : "隔壁張小二"} , {"sex" : "male"}]
複製代碼
結論:使用 concat() 方法拷貝數組,而後修改新數組,會改變舊數組的值。

三、使用 assign() 方法

拷貝數組:

let arr4 = Object.assign({} , arr1);
console.log(arr4);   //打印新數組
[1 , 2 , 3 , {"name" : "張小二"} , {"sex" : "male"}];  //新數組
複製代碼

修改通過 assign() 拷貝過的新數組

arr4[3].name = "隔壁張小二";
console.log(arr1);  //打印舊數組
[1 , 2 , 3 , {"name" : "隔壁張小二"} , {"sex" : "male"}]   
console.log(arr4);   //打印新數組
[1 , 2 , 3 , {"name" : "隔壁張小二"} , {"sex" : "male"}]
複製代碼
結論:使用 assign() 方法拷貝數組,而後修改新數組,會改變舊數組的值。

3、緣由分析

一、數組的淺拷貝

(1)數組的直接賦值屬於數組的淺拷貝,JS存儲對象都是存內存地址的,因此淺拷貝會致使新數組和舊數組共用同一塊內存地址,其中一個數組變化,另外一個數組也會相應的變化。

(2)數組內部不含有引用類型,使用slice() 、concat() 和 assign() 方法都屬於數組的深拷貝,一個數組變化,另外一個數組不受影響。

(3)數組內部含有引用類型,使用slice() 、concat() 和 assign() 方法,非引用類型的值屬於深拷貝,引入類型的值屬於淺拷貝,一個數組變化,另外一個也會相應的變化。

4、解決辦法(含有引入類型的數組)

方法一:遞歸

let cloneObj = function(obj){
    let str, newobj = obj.constructor === Array ? [] : {};
    if(typeof obj !== 'object'){
        return;
    } else if(window.JSON){
        str = JSON.stringify(obj), //系列化對象
        newobj = JSON.parse(str); //還原
    } else {
        for(var i in obj){
            newobj[i] = typeof obj[i] === 'object' ? 
            cloneObj(obj[i]) : obj[i]; 
        }
    }
    return newobj;
};

let newArr = cloneObj(oldArr);

複製代碼

方法二:經過JSON解析解決

let newArr = JSON.parse(JSON.stringify(oldArr));
複製代碼
注意:這種方法拷貝後的數組會丟失原數組中定義的方法和數組原型中定義的方法。
相關文章
相關標籤/搜索