当前位置 : 主页 > 网络编程 > JavaScript >

JavaScript深拷贝与浅拷贝原理深入探究

来源:互联网 收集:自由互联 发布时间:2023-01-30
目录 一、JS中数据的存储形式-堆栈 二、深浅拷贝的三种方式 遍历赋值 Object.create() 遍历赋值实现深拷贝 一、JS中数据的存储形式-堆栈 我们先简单理解一下堆栈分别是啥: 什么是栈:
目录
  • 一、JS中数据的存储形式-堆栈
  • 二、深浅拷贝的三种方式
    • 遍历赋值
    • Object.create()
    • 遍历赋值实现深拷贝

一、JS中数据的存储形式-堆栈

我们先简单理解一下堆栈分别是啥:

什么是栈:计算机为原始类型开辟的一块内存空间 string number ...

什么是堆:计算机为引用类型开辟的一块内存空间 object

我们分别分析下面两段代码:

var a = 'jack'
var b = a
b = 'andy'
console.log(a,b);//jack andy
var c = {key : 1}
var d = c
d.key = 2
console.log(c,d);//{ key: 2 } { key: 2 }

看完之后我们可能有这么一个疑问,第一段代码很好理解,但是第二段代码里改变的明明是d中的key,为啥c里的key也改变了呢?

这里就是因为堆跟栈的原理,我们在定义原始类型数据的时候都会开辟一个栈的空间,当声明一个基本变量时,它就会被存储到栈内存中,而当其发生复制时,会把对应内存中的数据复制一份到新内存中,所以这两个变量之间没有什么联系。如果我们对引用类型进行复制,我们只是将地址复制了一遍,原变量和复制的新变量都是指向同一个地址,这就说明对新变量进行修改时就会影响到原变量的值。

二、深浅拷贝的三种方式

  • 遍历赋值
  • Object.create()
  • JSON.parse()和JSON.stringify()

深拷贝与浅拷贝,简单点来说:

就是假设B赋值了A,当修改A时,看B是否会发生变化,如果B也跟着变了,说明这是浅拷贝。如果B没变,那就是深拷贝。

遍历赋值

下面我们看一段代码:

var obj = {
  a:'hello',
  b:{
    a:'world',
    b:111
  },
  c:[11,'jack','abdy']
}
function clone(obj) {
  var objnew = {}
  for(var i in obj) {
    objnew[i] = obj[i]
  }
  return objnew
}
var objcopy = clone(obj)
console.log(obj);
console.log(objcopy);

在这里我们定义了一个对象 obj,为了实现能够复制出另一个对象,我们再定义一个clone方法。然后调用这个方法,输出原来的对象和复制后的对象,看看是否相同:

在控制台下输出的两个结果没有任何差异,那他们两个是否真的完全一样呢?

他们两个是有不同的,因为这里的拷贝属于浅拷贝,我们根据浅拷贝的定义,如果在上例中,我们改变了objcopy,那么obj也发生了变化的话那他就是一个浅拷贝。

我们下面来验证一下,改变一下objcopy.b.a的值,看看 obj 里会不会发生相应的变化:

将objcopy.b.a赋值为字符串hhhh后,obj.b.a也变为了hhhh,这就说明我们最开始的拷贝属于浅拷贝。因为obj.b他是一个引用类型,所以objcopy.b和obj.b指向的都是同一个地址,这样不管objcopy.b.a改成什么,obj.b.a都会变成什么。

Object.create()

这种方法只需要行代码:

var objcopy = Object.create(obj)

obj在复制的时候,它会被当前对象复制到原型__proto__上,并不是复制到当前的对象上。我们再次改变一下objcopy.b.a的值,看看 obj 里会不会发生相应的变化:

所以他也是浅拷贝

遍历赋值实现深拷贝

这是深拷贝的克隆函数:

function deepclone(startobj,endobj) {
  var obj = endobj || {}
  for(var i in startobj)
  {
    if(typeof startobj[i] === 'object') {
      obj[i] = startobj[i].constructor === Array ? [] : {}
      deepclone(startobj[i],obj[i])
    }else {
      obj[i] = startobj[i]
    }
  }
  return obj
}

值得注意的一点是,在递归调用的时候,需要把当前处理的 obj[i] 给传回去,否则的话 每次递归obj都会被赋值为空对象,就会对已经克隆好的数据产生影响。

我们来验证一下是否是深拷贝:

var objcopy = deepclone(obj)
objcopy.b.a = 'hhhh'
console.log(obj);
console.log(objcopy);

运行结果:

这就说明我们的深拷贝已经实现了。

通过JSON.parse()和JSON.stringify()实现深拷贝

这是我们在工作中最常见的一种方式

var objcopy = JSON.parse(JSON.stringify(obj))

我们对初始化的对象先通过JSON.stringify转换为字符串,再通过JSON.parse转回对象类型。

这样为什么能实现深拷贝的效果呢?因为我们通过 JSON.stringify 转成 string 类型后,他就存储在栈里,这样就不会出现地址值上的误会了,再通过JSON.parse就可以再转换为object类型。

到此这篇关于JavaScript深拷贝与浅拷贝原理深入探究的文章就介绍到这了,更多相关JS深拷贝与浅拷贝内容请搜索自由互联以前的文章或继续浏览下面的相关文章希望大家以后多多支持自由互联!

上一篇:JavaScript自定义Promise实现流程
下一篇:没有了
网友评论