javascript中的深拷贝与浅拷贝

javascript中的深拷贝与浅拷贝

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

声明:参考文章:http://www.cnblogs.com/Chen-XiaoJun/p/6217373.html

看这个深拷贝浅拷贝之前,先要对javascript中不同数据类型之间的传值有一定的了解

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

javascript中数据类型简单分为基本数据类型引用数据类型

基本数据类型:String,Number,Boolean等

引用数据类型:Array,Object等

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

基本数据类型的传值

1
2
3
4
5
6
7
8
var a = 1
var b = a
// a = 1
// b = 1
b = 2
// a = 1
// b = 2
// a 的原始值不会被修改

引用数据类型的传值

1
2
3
4
5
6
7
8
9
10
11
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的原始值,这就是浅拷贝

深拷贝和浅拷贝

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

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
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对象中,很麻烦,容易出错,这时候就要有深拷贝的方法浅拷贝的方法

浅拷贝的实现

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

  2. Object.assign()

    1. 介绍Object.assignES6的新函数,Object.assign() 方法可以把任意多个的源对象自身的可枚举属性拷贝给目标对象,然后返回目标对象,但是 Object.assign() 进行的是浅拷贝,拷贝的是对象的属性的引用,而不是对象本身
    2. 语法Object.assign(target, ...sources)
    3. 参数target:目标对象,sources:任意多个源对象
    4. 返回值:返回目标对象
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    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()去处理只有一层的拷贝的时候,再看看

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    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

深拷贝的实现

  1. 手动复制,这在上面介绍传值的时候,有过例子,可以参照上面的例子,缺点:很麻烦
  2. 对象只有一层的话,可以用上面的Object.assign()去处理
  3. 利用JSON字符串的基本类型赋值
    1. 优点:理解简单,用起来也简单
    2. 缺点:只适用于那些能够被 json 直接表示的数据结构,且它会抛弃对象的constructor
1
2
3
4
5
6
7
8
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. 递归拷贝
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
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)

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

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

改进如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
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()方法的原理其实就和上面递归拷贝实现改进版是一样的

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
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
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
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 的值没被修改

本文标题:javascript中的深拷贝与浅拷贝

文章作者:王工头

发布时间:2019年01月26日 - 22:20:51

最后更新:2019年01月26日 - 23:32:06

原始链接:https://qqqww.com/javascript中的深拷贝与浅拷贝/

许可协议: 署名-非商业性使用-禁止演绎 4.0 国际 转载请保留原文链接及作者。

-------------本文结束感谢大佬们的阅读-------------