javascript中的深拷贝与浅拷贝

欢迎访问我的博客https://qqqww.com,祝码农同胞们早日走上人生巅峰,迎娶白富美~~~

1 javascript中不同数据类型之间的传值

说明:看这个深拷贝浅拷贝之前,先要对javascript中不同数据类型之间的传值有一定的了解
javascript中数据类型简单分为基本数据类型引用数据类型
基本数据类型:String,Number,Boolean等

引用数据类型:Array,Object等

基本数据类型引用数据类型最大的区别在于他们的处安置方式不同

基本数据类型的传值

var a = 1
var b = a
// a = 1
// b = 1
b = 2
// a = 1
// b = 2
// a 的原始值不会被修改

引用数据类型的传值

var people = {
    name: 'zhangsan',
    age: 10
}
var people2 = people
console.log(people.age) // 10
console.log(people2.age) // 10
people2.age = 100
console.log(people.age) // 100
console.log(people2.age) // 100
// people 的值被修改了

这里发现 people 的值被修改了,因为这就是引用传值,他们本身指向了同一个引用,即地址,修改的是同一个地址中的值,所以会发生想修改people2的时候,却无意间影响到了people的原始值,这就是浅拷贝

2 深拷贝和浅拷贝

如上例子,简单粗暴的理解就是当people2复制了people,而当people2修改时,people一块被修改了,这叫浅拷贝,当people2修改时,而people没有被修改,这叫深拷贝

但是,本意不想修改people,只想修改people2的,却造成了这样的错误,如何避免呢?

var people = {
    name: 'zhangsan',
    age: 10
}
var people2 = {
    name: people.name,
	age: people.age
}
var people2 = people
console.log(people.age) // 10
console.log(people2.age) // 10
people2.age = 100
console.log(people.age) // 10
console.log(people2.age) // 100
// people 的值没有被修改

这个时候是深拷贝,但是总不能总不能这么复杂吧,要一个一个的拷贝到people2对象中,很麻烦,容易出错,这时候就要有深拷贝的方法浅拷贝的方法

2.1 浅拷贝的实现

  1. 赋值,上面介绍传值的时候也有的方法,就是直接赋值的操作,可以参照上面的例子

  2. Object.assign()

    1. 介绍Object.assignES6的新函数,Object.assign() 方法可以把任意多个的源对象自身的可枚举属性拷贝给目标对象,然后返回目标对象,但是 Object.assign() 进行的是浅拷贝,拷贝的是对象的属性的引用,而不是对象本身
    2. 语法Object.assign(target, ...sources)
    3. 参数target:目标对象,sources:任意多个源对象
    4. 返回值:返回目标对象
    var people = {
        name: 'zhangsan',
        age: 10,
        zhangsan: {
            say: function () {
                console.log('hello')
            },
            height: 180
        }
    }
    var people2 = Object.assign({}, people)
    console.log(people.zhangsan.height) // 180
    console.log(people2.zhangsan.height) // 180
    people2.zhangsan.height = 181
    console.log(people.zhangsan.height) // 181
    console.log(people2.zhangsan.height) // 181
    // people 的值被修改了
    

    但是当Object.assign()去处理只有一层的拷贝的时候,再看看

    var people = {
        name: 'zhangsan',
        age: 10
    }
    var people2 = Object.assign({}, people)
    console.log(people.age) // 10
    console.log(people2.age) // 10
    people2.age = 100
    console.log(people.age) // 10
    console.log(people2.age) // 100
    // people 的值没有被修改
    

    总结

    Object.assign()去处理一层的拷贝的时候,可以达到,修改people2不影响people,但是当Object.assign()进行深一层的拷贝的时候,修改people2会影响people

2.2 深拷贝的实现

  1. 手动复制,这在上面介绍传值的时候,有过例子,可以参照上面的例子,缺点:很麻烦
  2. 对象只有一层的话,可以用上面的Object.assign()去处理
  3. 利用JSON字符串的基本类型赋值
    1. 优点:理解简单,用起来也简单
    2. 缺点:只适用于那些能够被 json 直接表示的数据结构,且它会抛弃对象的constructor
var people = { zhangsan: { age: 10 } }
var people2 = JSON.parse(JSON.stringify(people))
console.log(people.zhangsan.age) // 10
console.log(people2.zhangsan.age) // 10
people2.zhangsan.age = 100
console.log(people.zhangsan.age) // 10
console.log(people2.zhangsan.age) // 100
// people.zhangsan.age 原始值没被修改
  1. 递归拷贝
    function deepClone(a, b) {    
      var obj = b || {}   
      for (var i in a) {        
        if (typeof a[i] === 'object') {
          obj[i] = (a[i].constructor === Array) ? [] : {}           
          arguments.callee(a[i], obj[i])
        } else {
          obj[i] = a[i]
        }
      }    
      return obj
    }
    var str = {}
    var obj = { a: {a: "hello", b: 21} }
    deepClone(obj, str)
    console.log(str.a)

上述方法已经能够实现,深拷贝,但是当两个对象相互引用的时候,问题就来了,会出现死循环

为了避免相互引用的对象导致死循环的情况,则应该在遍历的时候判断是否相互引用对象,如果是则退出循环

改进如下

    function deepClone(a, b) {    
      var obj = b || {}    
      for (var i in a) {        
        var prop = a[i]        // 避免相互引用对象导致死循环,如a.a = a的情况
        if(prop === obj) {     // 避免相互引用对象导致死循环,如a.a = a的情况      
          continue
        }        
        if (typeof prop === 'object') {
          obj[i] = (prop.constructor === Array) ? [] : {}            
          arguments.callee(prop, obj[i])
        } else {
          obj[i] = prop
        }
      }    
      return obj
    }
    var str = {}
    var obj = { a: {a: "hello", b: 21} }
    deepClone(obj, str)
    console.log(str.a)
  1. 使用Object.create()方法

Object.create()方法的原理其实就和上面递归拷贝实现改进版是一样的

var people = {
    name: 'zhangsan',
    age: 10,
    zhangsan: {
        say: function () {
            console.log('hello')
        },
        height: 180
    }
}
var people2 = Object.create(people)
console.log(people.zhangsan.height) // 180
console.log(people2.zhangsan.height) // 180
people2.zhangsan.height = 181
console.log(people.zhangsan.height) // 180
console.log(people2.zhangsan.height) // 181
// people 的值没被修改
  1. JQuery 中的 $.extend
var $ = require('jquery')
var people = {
    name: 'zhangsan',
    age: 10,
    zhangsan: {
        say: function () {
            console.log('hello')
        },
        height: 180
    }
}
var people2 = $.extend(true, {}, people)
console.log(people.zhangsan.height) // 180
console.log(people2.zhangsan.height) // 180
people2.zhangsan.height = 181
console.log(people.zhangsan.height) // 180
console.log(people2.zhangsan.height) // 181
// people 的值没被修改

3 参考文章

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值