学习一下引用数据类型拷贝

本文详细探讨了JavaScript中的引用数据类型拷贝,包括赋值、浅拷贝(通过Object.assign()、展开运算符...及Array.prototype.slice()实现)和深拷贝。文中列举了各种拷贝方法的示例,并分析了它们的特点,如浅拷贝只复制一层,而深拷贝能实现多层复制。此外,还提到了深拷贝的一些限制,如无法处理函数、循环引用、Date对象和正则表达式,并给出了解决方案和优化建议。
摘要由CSDN通过智能技术生成

对于引用数据类型,细分可以分为以下三个方面

  • 赋值
  • 浅拷贝
  • 深拷贝

目录

赋值:

浅拷贝

Object.assign()

展开运算符...

Array.prototype.slice()

深拷贝

总结 


赋值:

只是改变指针的指向,例如,引用数据类型的赋值是对象保存在栈种的赋值,这样的化两个变量就都指向同一个对象,因此彼此之间的操作互有影响。

举个例子:

var a = {};
var b = a;

a.name = "ls";
console.log(a.name); // "ls"
console.log(b.name); // "ls"

b.age = 22;
console.log(b.age); // 22
console.log(a.age); // 22

console.log(a==b)  //true

 这种情况会导致a 和 b指向相同的数据,对其中一个修改的话,会影响到另外一个。实际开发的时候,这会让人很难受。

怎样才能让他们会不影响呢? 一个简单的方法就是复制一份a 变量的数据,这就是拷贝。

所以根据拷贝的层次不同可以分成浅拷贝和深拷贝,浅拷贝只进行一层拷贝,深拷贝则进行无限层次的拷贝。

浅拷贝

先实现一个浅拷贝:

let shallowClone = source => {
    let target = {}
    for(let i in source){
        if ( source.hasOwnProperty(i) ){ //obj.hasOwnProperty,返回值是一个布尔值,即是否是obj的属性(原型上的是false)
            target[i] = source[i];
        }
    }
    return target;
}

let demo ={
    b:{
        c:{}
    }
}

let demo2 = shallowClone(demo);
let demo3 = demo;

console.log(demo3 === demo)            //true
console.log(demo2.b.c === demo.b.c)    //true
console.log(demo2.b === demo.b)        //true
console.log(demo2 === demo)            //false

demo3 = demo 这样赋值,是地址赋值,demo3 、demo指向同一个对象,这不是我么想要的,我们看一下shallowClone函数,只是个浅拷贝的实现方式,那么demo2变量应该是实现了一个副本,demo2变量是在堆中开了一个新的内存,所以指向其他对象,demo2 === demo.b 为 true说明这就是浅拷贝的效果,简单拷贝了一层。


Object.assign()

Object.assign() 方法用于将所有可枚举属性的值从一个或多个源对象复制到目标对象,返回目标对象。

举个例子:

let demo = {
    name:"lsbook",
    book:{
        title:"Do you know JS",
        price:"45"
    }
}

let clone_demo = Object.assign({},demo);
console.log(clone_demo);
demo.name = "new name"
demo.book.price = "100"
console.log(clone_demo.name,clone_demo.book.price)  // lsbook 100

修改上面的代码demo变量之后,对象clone_demo基本属性没有改变,但是修改demo对象中书引用属性时,对象clone_demo相应位置属性值也发生改变 

展开运算符...

与Object.assign()效果相同

let demo = {
    name:"lsbook",
    book:{
        title:"Do you know JS",
        price:"45"
    }
}

let clone_demo = {...demo}
console.log(clone_demo);
demo.name = "new name"
demo.book.price = "100"
console.log(clone_demo.name,clone_demo.book.price)  // lsbook 100

Array.prototype.slice()

slice() 方法返回一个新的数组对象,该对象由begin和end决定的原先的浅拷贝。原数组不会被改变。

let a = [0,"1",[2,3]];
let b =a.slice(1);
console.log(b)     //["1",[2,3]]


a[1] = "4";
a[2][0] = 5;
console.log(a);  // [0,"4", [5,3]]
console.log(b);  // ["1",[2,3]]

深拷贝

当对象和它引用的对象一起复制时即发生深拷贝。拷贝前后两个对象互不影响。

JSON.parse(JSON.stringify (obj))

let dome = {
    name:"lsbook"
    book:{
        title:"Do you know JS",
        price:45
    }
}

let clone_demo = JSON.parse(JSON.stringify(demo));
console.log(clone_demo);
demo.name = "new name"
demo.book.price = 100
console.log(clone_demo.name,clone_demo.book.price);  // ls  45

在demo改变之后对clone_demo完全没有影响,这就是深拷贝。

值得注意的是:

  • 会忽略undefined  Symbol
  • 不能序列化函数
  • 不能解决循环引用的对象
  • 不能正确处理new Date()
  • 不能处理正则

 对于undefined  symbol  函数 三种情况会直接忽略

let demo = {
    name : "ls",
    h1 : undefined,
    h2 : Symbol("ls"),
    h3 : function () {},
}

let clone_demo = JSON.parse(JSON,stringify(demo));
console.dir(clone_demo)  // {name : "ls"}

循环引用情况下会报错

let obj = {
    a : 1,
    b : {
        c : 2,
        d : 3
    }
}

obj.a = obj.b;
obj.b.c = obj.a;


let obj2 = JSON.parse(JSON.stringify(obj)); // Uncaught TypeError: Converting circular structure to JSON

new Date 情况下,转换结果不正确

new Date();
// Wed Jul 01 2020 16:19:07 GMT+0800 (中国标准时间) {}

JSON.stringify(new Date())
// ""2020-07-01T08:19:19.860Z""

JSON.parse(JSON.stringify(new Date()))
// "2020-07-01T08:19:35.569Z"

解决方法 将时间转换成字符串

let date = (new Date()).valueOf();
// 1593591638596

JSON.stringify(date);
// "1593591638596"

JSON.parse(JSON.stringify(date));
// 1593591638596

正则情况下

let demo = {
    name:"ls",
    a: /'123'/
}

console.log(demo)   // {name: "ls", a: /'123'/}

let clone_demo = JSON.parse(JSON.stringify(obj));
console.log(clone_demo);
// {name: "ls", a: {}}

实现一个简单的深拷贝:

   浅拷贝+递归的方法。浅拷贝的时候判断是不是对象,是对象就递归。

let shallowClone = source =>{
    let target = {};
    for (let key in source){
        if(Object.prototype.hasOwnProperty.call(source, key)){
             target[key] = typeof source[key] === 'object'? shallowClone(source[key]) : source[key];
        }
    }
    return target
}

let demo = {
    name : "ls",
    book : {
        title : 'Do you know JS',
        price : "45"
    }
}

let clone_demo = shallowClone(demo);
console.log(clone_demo);

demo.name = "qqq"
demo.book.price = "50"

console.log(clone_demo.name,clone_demo.book.price) // ls 45

上述代码还有问题的

  • 没有考虑并发症写法
  • 对对象的判断不够严谨,因为  typeof null === object
  • 没有对预先设定的参数校正,可能会有null应返回null而不是{}

首先需要一个兼容的合并和判断 null 方法的函数

let isObject = obj => typeof obj === 'object' && object != null

把它加到我们的代码中

 

let isObject = obj => typeof obj === 'object' && obj != null;
let shallowClone = source =>{
    if (!isObject(source)) return source;
    let target = Array.isArray(source) ? []:{};     
    for (let key in source){
        if(Object.prototype.hasOwnProperty.call(source, key)){
             target[key] = isObject(source[key])? shallowClone(source[key]) : source[key];
        }
    }
    return target
}

let demo = {
    name : "ls",
    book : {
        title : 'Do you know JS',
        price : "45"
    },
    h1 : null,
    h2 : undefined,
    h3 : [1,2,3]
}

let clone_demo = shallowClone(demo);
console.log(clone_demo);

demo.name = "qqq"
demo.book.price = "50"
demo.h3[1] = "55"

console.log(clone_demo.name,clone_demo.book.price) // ls 45

console.log(clone_demo)

我们试着去优化JSON.parse(JSON.stringify(obj))循环引用引发异常的问题。

let isObject = obj=>typeof obj ==='object' && obj !=null;

let shallowClone = (source,hash = new WeakMap()) =>{
    if (!isObject(source)) return source;
    if(hash.has(source)) return hash.get(source); //检测,查哈希表
    let target = Array.isArray?[]:{};
    hash.set(source,target) //设置哈希表
    for (let key in source){
        if(Object.prototype.hasOwnProperty.call(source,key)){
            target[key] = isObject(source[key])? shallowClone(source[key],hash) : source[key]  //传入哈希表
        }
    }
    return target
}

let obj = {
    a : 1,
    b : {
        c: 2,
        d: 3
    }
}

obj.a = obj.b;
obj.b.c = obj.a;

let clone_obj = shallowClone(obj)
console.log(clone_obj);

总结 

-和原数据是否指向相同第一层数据为基本数据类型原数据中包含子对象
赋值改变原数据共同改变改变原数据共同改变
浅拷贝改变不会使原数据一同改变改变原数据共同改变
深拷贝改变不会使原数据一同改变改变不会使原数据一同改变

 

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值