1. 程式人生 > 其它 >精緻的js深淺拷貝(面試題有關)

精緻的js深淺拷貝(面試題有關)

技術標籤:JavaScriptjavascript

詳解js中的拷貝

一、深拷貝與淺拷貝的區別

通俗易懂的來說就是假設B複製了A,當修改B時,看A是否會發生變化,如果A也跟著變了,說明這是淺拷貝,如果A沒變,那就是深拷貝。

學過js的同學應該都知道,javascript變數包含兩種不同資料型別的值:基本資料型別和引用資料型別

基本資料型別採用的是值傳遞,建立一個基本資料型別會在記憶體中開闢一個新的空間;而引用資料型別採用的是地址儲存,如果兩個引用資料型別使用的是同一個地址的話,改變其中一個的數值也會改變另一個。

由此可見,深拷貝本身只針對較為複雜的object型別資料,也就是引用資料型別。

二、淺拷貝的實現

1、基本資料型別

var a=1;
var b=a;
console.log(a,b)           // 1,1
b=2;
console.log(b)             // 2
console.log(a)             // 1

基本資料型別的淺拷貝就是賦值操作,兩個是互不相干的。

2、引用資料型別

①直接賦值法

var arr1 = [1,2,3,4];
var arr2 = arr1;
console.log(arr1,arr2)	   // [1,2,3,4],[1,2,3,4]
arr2.pop(4);
console.log(arr2)          // [1,2,3]
console.log(arr1) // [1,2,3]

引用資料型別之間的直接賦值,是將一個數據的地址賦給了另一個數據,所以其中一個改變了其中的值之後,另一個也隨之改變。

②Object.assign()

var obj = {
    a: 1,
    b: 2
}
var obj1 = Object.assign(obj);
obj1.a = 3;
console.log(obj.a)  	   // 3

Object.assign() 方法用於將所有可列舉屬性的值從一個或多個源物件複製到目標物件。它將返回目標物件。

三、深拷貝的實現

1、陣列中只包含基本資料型別

①、迴圈陣列,包含for迴圈但不僅有for迴圈

let arr1= [1,2,3,4];
let arr2= forLoop(arr1);
function forLoop(arr){
	let res = [];
	for(let i=0,length=arr.length;i<length;i++){
		res.push(arr[i]);
	}
	return res;
}

②、陣列的slice方法

let arr1= [1,2,3,4];
let arr2 = arr1.slice(0);

③、陣列的concat方法

let arr1= [1,2,3,4];
let arr2 = arr1.concat();

④、陣列的from方法

如果引數是一個真正的陣列,Array.from會返回一個一模一樣的新陣列

let arr1= [1,2,3,4];
let arr2 = Array.from(arr1);

⑤、擴充套件運算子

let arr1 =[1,2,3];
let [...arr2] = arr1;

⑥、jQuery的extend方法

jQuery的extend方法是個特例,當第一個引數為false時,只支援陣列內為基本資料型別

var arr1= [1,2,3,4];
var newArray = $.extend(false,[],array); 

2、物件中只包含基本資料型別

①、for迴圈,將物件的值拿出來

let obj1={count:1,name:'grace',age:1};
let obj2 = copyObj(obj){
	let res = {};
	for(let key in obj){
		res[key]=obj[key];
	}
	return res;
}

②、Object.assign()

var obj = {
    a: 1,
    b: 2
}
var obj1 = Object.assign({}, obj);    	// obj賦值給一個空{}
obj1.a = 3;
console.log(obj.a)// 1

③、擴充套件運算子

let obj1={count:1,name:'grace',age:1};
let {...obj2} = obj1;

④、手動賦值

let obj1 = {
   a: 1,
   b: 2
}
let obj2 = {
   a: obj1.a,
   b: obj1.b
}
obj2.a = 3;
alert(obj1.a); // 1
alert(obj2.a); // 3

⑤、jQuery的extend方法

var obj1= {name:'peter',age:18};
var obj2= $.extend(false,{},obj1); 

3、當物件或數組裡含有一層或多層引用資料型別時(重點)

一般來說,物件或數組裡不可能只包含基本資料型別,更多的包括多個引用資料型別,而這種的深拷貝才是最重要的。

①、jQuery的extend方法

之前說了,jQuery的extend方法是一個特殊的方法,當第一個引數為true時,可以對具有引用資料型別的物件或陣列中進行深拷貝。

var result=$.extend( true, {}, { name: "John", location: {city: "Boston",county:"USA"} }, 
{ last: "Resig", location: {state: "MA",county:"China"} } );

console.log(result) 
//{name:"John",last:"Resig",location:{city:"Boston",state:"MA",county:"China"}}

②、JSON.parse(JSON.stringify())

JSON.parse(JSON.stringify())這個方法生成了一個新的空間,所以可以對具有引用資料型別的物件或陣列中進行深拷貝。

let obj1 = {count:1,name:'grace',arr:[1,2,3]};
let obj2 = JSON.parse(JSON.stringify(obj1));
obj1.arr.pop()
console.log(obj2)      //{count:1,name:'grace',arr:[1,2,3]}

③、遞迴方法

//任何引用資料型別的值都需要挨個遍歷,直到取到基本型別的值為止
function deepClone( obj ){
    let cloneObj = Array.isArray( obj ) ? [] : {};
    // 判斷引數obj不能是null
    if( typeof (obj) === 'object' && obj !== null ){
        //遍歷引數obj
        for( let key in obj ){
            if( typeof( obj[key] )=== 'object' ){
                // cloneObj['hobby'] = obj['hobby'] 陣列直接賦值不行的
                cloneObj[key] = deepClone(obj[key]);
            }else{   //基本資料型別的值
                cloneObj[key] = obj[key];
            }
        }
    }
    return cloneObj;
}
let obj3 = deepClone(obj1);

總結至此,常用的深淺拷貝方法就在這了,其實還有其他的一些深淺拷貝的方法,如果感興趣的話,可以繼續去挖掘哦~

想和小編討論的,評論區等著你~