JS深浅拷贝

1.1 栈堆、基本数据类型、引用数据类型

        栈堆:存放数据的地方

        基本数据类型:number,string,boolean,null,undefined

        引用数据类型:function,Array,object

        基本数据类型保存在栈内存,引用类型保存在堆内存中。根本原因在于保存在栈内存的必须是大小固定的数据,引用类型的大小不固定,只能保存在堆内存中,但是可以把它的地址写在栈内存中以供访问。

1.2 赋值

        将某一对象赋给某个变量的过程,称为赋值。

        当将一个对象赋值给另外一个新的变量时,赋的其实是该对象在栈中的地址,而不是堆中的数据。也就是说,两个对象同时指向一个存储空间,是联动的,无论修改哪个对象都会影响另一个对象(针对引用数据类型)

        如下例:将a赋值给c,无论是修改a,还是修改c,两者都发生变化

var a=[1,2,3,4,5];
var c=a
console.log(a)//[1, 2, 3, 4, 5]
console.log(c)//[1, 2, 3, 4, 5]
c[0]=8
console.log(a)//[8, 2, 3, 4, 5]
console.log(c)//[8, 2, 3, 4, 5]
a[0]=10
console.log(a)//[10, 2, 3, 4, 5]
console.log(c)//[10, 2, 3, 4, 5]

1.3 浅拷贝

        创建一个对象,这个对象有着原始对象属性值的一份精准拷贝,如果属性是基本类型,拷贝的就是基本类型的值;如果属性是引用类型,拷贝的就是内存地址,如果其中一个对象改变了这个地址,就会影响到另一个对象(属性是引用类型的情况)。

        浅拷贝会重新在堆中创建内存,拷贝前后对象的基本数据类型互不影响,但拷贝前后对象的引用类型因共享同一块内存,会相互影响。

         浅拷贝与赋值的区别:浅拷贝创建了一个新的对象,指向一个新的房间,赋值是没有的。

//-------方法1-------
function shallowClone(source){
    if(!source || typeof source !== 'object'){
        return source;
    }
    let newObj = source.constructor=== Array ?[]:{};
    //for in 循环遍历一个对象的可枚举属性,包括这个对象继承自原型链上的可枚举属性
    for(var i in source){
        if(source.hasOwnProperty(i)){
            newObj[i]=source[i]
        }
    }
    return newObj;
}
var person = {'name':'张三','age':[1,2,3,4,5]}
var person1 = shallowClone(person)
//name为基本数据类型
console.log(person.name)//张三
console.log(person1.name)//张三

person1.name="李四";
console.log(person.name)//张三
console.log(person1.name)//李四

person.name='王五';
console.log(person.name)//王五
console.log(person1.name)//李四

//age为引用数据类型,改变一个,影响另一个
console.log(person.age)//[1,2,3,4,5]
console.log(person1.age)//[1,2,3,4,5]

person1.age[0]=6;
console.log(person.age)//[6,2,3,4,5]
console.log(person1.age)//[6,2,3,4,5]

person.age[0]=7;
console.log(person.age)//[7,2,3,4,5]
console.log(person1.age)//[7,2,3,4,5]

浅拷贝实现方式: 

JavaScript浅拷贝与深拷贝的几种方法。_喜陈的博客-CSDN博客_js深拷贝和浅拷贝的方法  

let person = {'name':'张三','age':[1,2,3,4,5]}
let arr=['one','two',['three'],{name:'Bob'}]

// 1.Object.assign()
let newObj1 = Object.assign({}, person);
// 2.展开运算符...
let newObj2 = {...person};
// 3.array.prototype.concat()
let newObj3 = arr.concat();
// 4.array.prototype.slice()
let newObj4 = arr.slice();

//测试
arr[0] = '一'
arr[2][0] = '三'
arr[3].name = 'Jack'
console.log(arr);// ['一', 'two',['三'],{name: 'Jack'}]
console.log(newObj4);// ['one', 'two',['三'],{name: 'Jack'}]

 问题:下例中也是对引用数据类型的修改,为什么不互相影响?

var person = ['张三',[1,2,3,4,5]]
var person1=person.concat()

person1[1]=[1,2,2];
console.log(person[1])//[1,2,3,4,5]
console.log(person1[1])//[1,2,2]

person[1]=[4,5,6];
console.log(person[1])//[4,5,6]
console.log(person1[1])//[1,2,2]

       因为person1[1]存储的指针变了,而person[1]的指针没有变,这是直接替换了指针,并不是通过指针修改数据。引用数据类型的地址和值不存在一起,而是通过指针从地址指向值,person1[1]整体变化了,就是指针的指向变了。

1.4 深拷贝

        深拷贝就是将一个对象从内存中完整的拷贝一份出来,从堆内存中开辟一个新的区域存放新对象,且修改新对象不会影响原有的对象。(意思就是a,b指向不同的堆)

        深拷贝就不会像浅拷贝那样只拷贝一层,而是有多少层就拷贝多少层,真正的做到全部内容都放在自己新开辟的内存里,完成拷贝后不论如何修改都不会改变原有对象。可以利用递归思想实现深拷贝。

        方法1:JSON.parse(JSON.stringify())

        原理:用JSON.stringify将对象转成JSON字符串,再用JSON.parse()把字符串解析成对象,一去一来,新的对象产生了,而且对象会开辟新的栈,实现深拷贝。这种方法虽然可以实现数组或对象深拷贝,但不能处理函数这是因为JSON.stringify() 方法是将一个JavaScript值(对象或者数组)转换为一个 JSON字符串,不能接受函数。

function replace(key,value){
    if(typeof value==='function'){
        return value.toString()
    }
    if(typeof value==='undefined'){
        return 'undefined'
    }
    return value
}
var arr1 = ['red','green'];
var arr2 = JSON.parse(JSON.stringify(arr1));//复制
var arr3 = [1,2,3,['1','2','3']];
var arr4 = JSON.parse(JSON.stringify(arr3));//复制
var arr5={'look':'handsome','eat':function (){console.log("吃胖")},"xxx":undefined}
var arr6 = JSON.parse(JSON.stringify(arr5,replace));//复制
console.log(arr2,"------",arr4,"------",arr5)//['red','green'];
arr1.push('black') ;//改变color1的值
arr3[3][0]=0;
console.log(arr2,"------",arr4,"------",arr6)//["red", "green"]
console.log(arr1,"------",arr3)//["red", "green", "black"]

        方法2:递归实现深拷贝

function deepClone(obj){
    if(!obj || typeof obj !== 'object'){
        return obj;
    }
    //判断参数是不是一个对象
    //两种方法
    let objClone = obj.constructor=== Array ?[]:{};
    // let objClone = Array.isArray(obj)?[]:{};
    if(obj && typeof obj==="object"){
        for(const 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;
}
//深拷贝
var arr2 = {
    name: '测试深拷贝',
    age: 20,
    address: ['沈阳', '合肥'],
    friends: {
        friend1: '张三',
        friend2: '李四'
    },
    function(){
        console.log("我是深拷贝的对象")
    }
}

let b2=deepClone(arr2)
console.log("深拷贝b2:",b2)
b2.address[0]="深拷贝"
console.log("修改深拷贝b2:",b2)
console.log("原数组:",arr2)//原数组数据没变,不影响原数组

1.5 总结

        深拷贝和浅拷贝是针对复杂数据类型来说的,浅拷贝只拷贝一层,而深拷贝是层层拷贝。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值