天天看点

浅拷贝和深拷贝的区别和实现方法

对于这个问题可以从深拷贝和浅拷贝的使用或起源说起。

1. js变量包含两种不同数据类型的值基本类型和引用类型

基本类型包括ES6新增的一共是6种,具体如下

string ,number, null ,undefined, boolean ,symbol
           

引用类型为那些可能由多个值构成的对象,只有一种

object
           

将一个值赋给变量时,解析器必须先确定这个值是引用类型还是基本类型。

基本数据类型可以操作保存在变量中的实际的值

引用类型的值是保存在内存中的值,但是JavaScript不允许直接访问内存中的位置,就是说不能直接操作对象的内存空间。在操作对象时,我们实际上操作的是对象的引用而不是实际的值。

2. 变量存储方式–栈和堆

栈:自动分配内存空间,自动释放,里面存放的是基本类型的值和引用类型的地址

堆:动态分配的内存,大小不定,不会自动释放。里面存放引用类型的值。

浅拷贝和深拷贝的区别和实现方法

3. 值传递和址传递

基本类型和引用类型最大的区别就是传值和传址的区别

基本类型采用值传递

var a = 100;
var b = a;
b++;
console.log(a);  //100
console.log(b);  //101

           

引用类型则采用址传递,将存放在栈中的地址赋值给变量

var a = [1,2,3];
var b = a;
b.push(4);
console.log(a);  //[1,2,3,4]
console.log(b);  //[1,2,3,4]
           

这里我们本意只想给数组b添加一个元素,然而数组a也增加了。

分析: 由于a和b都是引用类型,采用址传递,赋值的时候是把a的地址赋值给b,即a和b指向同一个地址,而这个地址都指向堆内存中引用类型的值,当b改变这个值的同时,因为a的地址也指向这个值,所以a的值也跟着变化。

要解决上面出现的问题,就是要使用深拷贝或者浅拷贝了。

首先,浅拷贝和深拷贝都只针对于array、object这种复杂对象。

浅拷贝解决就是设置一个新的对象obj2,通过遍历的方式将obj1对象的值一一赋值给obj2对象。

以下为浅拷贝的实现,比较简单,其实就是遍历对象属性的问题

//数组的浅拷贝

var a = [1, 2, 3];
var b = [];
for(var i in a) {
	b[i] = a[i];
}
b.push(4);
console.log(a); // [1,2,3]
console.log(b); // [1,2,3,4]


//对象的浅拷贝

var obj1 = {
	''a:1,
	'b':2
};
var obj2 = {};
for (var i in obj1) {
	obj2[i] = obj[i];
}
obj2['c'] = 3;

console.log(obj1); // {a: 1, b: 2  }
console.log(obj2); // {a:1, b: 2, c: 3 }


           

但上面的代码只能进行一层拷贝,无法进行深层次的拷贝,封装函数后对数组的嵌套进行测试

function shallowClone (source) {

	var target = {};
	for (var i in source) {
		if(source.hasOwnProperty (i) ) {
			target[i] = source[i];
		}
	}
	return target;
}

var obj1 = {
	fruits: ['apple','banner'],
	num: 100
}
var obj2  = shallowClone(obj1)

obj2.fruits[0] = 'orange'
console.log(obj1.fruits[0]); //orange
console.log(obj2.fruits[0]); //orange

           

结果证明无法进行深层次的拷贝,这个时候我们可以使用深拷贝来完成

以下为实现深拷贝的方法

深拷贝的问题其实可以分解成两个问题,浅拷贝+递归,什么意思呢?

看下面三个实现方法

  1. 用for…in实现遍历和复制
function deepClone(obj){
    let objClone = Array.isArray(obj)?[]:{};
    if(obj && typeof obj==="object"){
        for(key in obj){
            if(obj.hasOwnProperty(key)){
                //判断ojb子元素是否为对象,如果是,递归复制
                if(obj[key]&&typeof obj[key] ==="object"){
                    objClone[key] = deepClone(obj[key]);
                }else{
                    //如果不是,简单复制
                    objClone[key] = obj[key];
                }
            }
        }
    }
    return objClone;
}    
let a=[1,2,3,4],
    b=deepClone(a);
a[0]=2;
console.log(a,b);
           
  1. JSON方法实现
let deepClone = function (obj) {
	    	let  temp = JSON.stringify(obj);
		    let result  = JSON.parse(temp);
		    return result;
}
let obj1 = {
    a: {
        age: 20,
        class: 1602
    },
    b: {
        age: 21,
        class: 1601
    }
};
let obj2 = deepClone(obj1);
console.log(obj2);
           
  1. 利用数组的Array.prototype.forEach进copy
let deepClone = function (obj) {
    let copy = Object.create(Object.getPrototypeOf(obj));
    let propNames = Object.getOwnPropertyNames(obj);
    propNames.forEach(function (items) {
        let item = Object.getOwnPropertyDescriptor(obj, items);
        Object.defineProperty(copy, items, item);

    });
    return copy;
};

let testObj = {
    name: "weiqiujuan",
    sex: "girl",
    age: 22,
    favorite: "play",
    family: {brother: "wei", mother: "haha", father: "heihei"}
}
let testRes2 = deepClone(testObj);
console.log(testRes2);