JS 深浅拷贝

芯片硅前验证技术与驱动工程师实战指南 硅前验证是芯片开发流程中的关键环节,通过pre-silicon simulation等技术手段在流片前验证设计正确性。其核心技术包括软件仿真、FPGA原型验证和硬件仿真器解决方案,涉及Linux驱动开发、芯片架构理解和波形分析等核心技能。作为连接芯片设计与系统集成的桥梁,硅前验证能早期发现设计缺陷,节省数百万美元流片成本。以Cadence Palladium为代表的硬件仿真器,通过专用服务器系统实现比纯软件方案快100-1000倍的仿真速度,为驱动工程师提供完整的外设模型验证环境。掌握硅前验证技术可显著提升 阅读详情

目录

深浅拷贝

浅拷贝

深拷贝

示例1

总结:

浅拷贝

深拷贝

深浅拷贝

浅拷贝

拷贝的是对象的指针,修改内容互相影响

深拷贝

整个对象拷贝到另一块内存空间中,修改内容不互相影响

示例1

如下例子:对对象直接复制后,导致原对象值发生改变
let a = {
  age: 1
}
let b = a
a.age = 2
console.log(b.age) // 2

解决办法一:
let a = {
  age: 1
}
let b = Object.assign({}, a)
a.age = 2
console.log(b.age) // 1

解决办法二:
let a = {
  age: 1
}
let b = { ...a }
a.age = 2
console.log(b.age) // 1

解决办法三:
let a = ['ant', 'bison', 'camel', 'duck', 'elephant']
let b = a.slice(1, 5)
console.log(b) // ["bison", "camel", "duck", "elephant"]

浅拷贝只解决了第一层的问题,但是如果遇到嵌套对象,就不行了,就得用深拷贝。

let a = {
  age: 1,
  jobs: {
    first: 'FE'
  }
}
let b = { ...a }
a.jobs.first = 'native'
console.log(b.jobs.first) // native

解决办法一:

let a = {
  age: 1,
  jobs: {
    first: 'FE'
  }
}
let b = JSON.parse(JSON.stringify(a))
a.jobs.first = 'native'
console.log(b.jobs.first) // FE

但是JSON.parse(JSON.stringify(object))方法也是有局限性的:

  1. 会忽略undefined和symbol
  2. 不能序列化函数
  3. 不能解决循环引用的对象

解决办法二:自己实现深拷贝函数(考虑了对象、数组、Symbol类型以及多层嵌套)

function deepClone(obj) {
  function isObject(o) {
    return (typeof o === 'object' || typeof o === 'function') && o !== null
  }

  if (!isObject(obj)) {
    throw new Error('非对象')
  }

  let isArray = Array.isArray(obj)
  let newObj = isArray ? [] : {}

  Reflect.ownKeys(obj).forEach(key => {
    newObj[key] = isObject(obj[key]) ? deepClone(obj[key]) : obj[key]
  })

  return newObj
}


const e = Symbol("e")
const f = Symbol.for("f")
let obj = {
  a: [1, 2, 3],
  b: {
    c: 2,
    d: 3
  }
}
obj[e] = 'localSymbol'
obj[f] = 'globalSymbol'


let newObj = deepClone(obj)
newObj.b.c = 1

console.log(newObj)  // { a: [ 1, 2, 3 ], b: { c: 2, d: 3 }, [Symbol(e)]: 'localSymbol', [Symbol(f)]: 'globalSymbol' }
console.log(newObj[e] === obj[e]) // true
console.log(obj.b.c) // 2

上述函数的问题是没有考虑循环引用以及来自原型链上的属性的拷贝。

let obj = {
  a: [1, 2, 3],
  b: {
    c: 2,
    d: 3
  }
}
obj.e = obj

let newObj = deepClone(obj)
console.log(newObj.e) // 2

>输出
RangeError: Maximum call stack size exceeded

let childObj = Object.create(obj)
let newObj = deepClone(childObj)

console.log('原对象:')
for(let key in childObj){
  console.log(childObj[key])
}
console.log('新对象:')
for(let key in newObj){
  console.log(newObj[key])
}

>输出
原对象:
[ 1, 2, 3 ]
{ c: 2, d: 3 }
新对象:

解决循环引用问题,我们可以额外开辟一个存储空间,来存储当前对象和拷贝对象的对应关系。
当需要拷贝当前对象时,先去存储空间中找,有没有拷贝过这个对象,如果有的话直接返回,如果没有的话继续拷贝,这样就巧妙化解的循环引用的问题。

这里使用Reflect.ownKeys() 获取所有的键值,同时包括 Symbol。

  • for…in 获取当前对象及其原型链上的所有可枚举属性
  • Object.keys 获取当前对象上的所有可枚举属性
  • Object.getOwnPropertyNames 获取当前对象上的所有可枚举和不可枚举属性
  • Object.getOwnPropertySymbols 获取当前对象上所有Symbol属性
  • Reflect.ownKeys 获取当前对象上所有可枚举、不可枚举属性以及Symbol属性
  • Object.prototypeOf 获取对象原型链上一级的对象
  • Reflect.prototypeOf 获取对象原型链上一级的对象

所有通过Object和Reflect方法获取对象的属性,都无法访问到对象原型链上的属性。

  • Object.keys 是获取到对象属性的所有方法中范围最小的一种方法
  • Reflect.ownKeys 是获取到对象属性的所有方法中范围最大的一种方法
  • 此外 Reflect.ownKeys = Object.getOwnPropertyNames + Object.getOwnPropertySymbols
function deepClone(obj, wm = new WeakMap()) {
  function isObject(o) {
    return (typeof o === 'object' || typeof o === 'function') && o !== null
  }

  if (!isObject(obj)) {
    throw new Error('非对象')
  }

  if (wm.has(obj)) return wm.get(obj); // 新增代码,查哈希表

  let isArray = Array.isArray(obj)
  let newObj = isArray ? [] : {}
  wm.set(obj, newObj); // 新增代码,哈希表设值

  Object.getOwnPropertySymbols(obj).forEach(symKey => {
    newObj[symKey] = isObject(obj[symKey]) ? deepClone(obj[symKey], wm) : obj[symKey]
  })

  //使用for in替换Reflect.ownKeys
  for( let key in obj){
    newObj[key] = isObject(obj[key]) ? deepClone(obj[key],wm) : obj[key]
  }

  return newObj
}

测试一下:

const e = Symbol('e')
const f = Symbol.for('f')
const g = Symbol.for('g')
let obj = {
  a: [1, 2, 3],
  b: {
    c: 2,
    d: 3,
  },
}
obj[e] = 'localSymbol'
obj[f] = 'globalSymbol'

let childObj = Object.create(obj)
childObj[g] = 'globalSymbol_'

let newObj = deepClone(childObj)


console.log('原对象:')
for (let key in childObj) {
  console.log(childObj[key])
}
while (childObj) { // 循环
  Object.getOwnPropertySymbols(childObj).forEach(symKey => {
    console.log(childObj[symKey])
  })
  childObj = Object.getPrototypeOf(Object(childObj))
}

console.log('新对象:')
for (let key in newObj) {
  console.log(newObj[key])
}
while (newObj) { // 循环
  Object.getOwnPropertySymbols(newObj).forEach(symKey => {
    console.log(newObj[symKey])
  })
  newObj = Object.getPrototypeOf(Object(newObj))
}


>输出
原对象:
[ 1, 2, 3 ]
{ c: 2, d: 3 }
globalSymbol_
localSymbol
globalSymbol
新对象:
[ 1, 2, 3 ]
{ c: 2, d: 3 }
globalSymbol_

此时,上述函数可以深拷贝当前对象或数组的所有可枚举属性、Symbol类型键,以及该对象原型链上的所有可枚举属性。但仍然有一个问题,就是不能拷贝原型链上的Symbol类型键。
需要使用Object.getPrototypeOf来循环获取上一级对象的Symbol类型键属性。

function deepClone(obj, wm = new WeakMap()) {
  function isObject(o) {
    return (typeof o === 'object' || typeof o === 'function') && o !== null
  }

  if (!isObject(obj)) {
    throw new Error('非对象')
  }

  if (wm.has(obj)) return wm.get(obj) // 新增代码,查哈希表

  let isArray = Array.isArray(obj)
  let newObj = isArray ? [] : {}
  wm.set(obj, newObj) // 新增代码,哈希表设值

  while (obj) {
    Reflect.ownKeys(obj).forEach(key => {
      if(obj.propertyIsEnumerable(key)){
        newObj[key] = isObject(obj[key]) ? deepClone(obj[key],wm) : obj[key]
      }
    })
    obj = Object.getPrototypeOf(Object(obj))
  }

  return newObj
}

至此,该函数可以深拷贝当前对象和它原型链上的所有可枚举属性及Symbol属性,结果大家可以去验证。

总结:

浅拷贝

  • Object.assign()
  • 扩展运算符 …
  • Array.prototype.slice()/Array.prototype.concat()

深拷贝

  • JSON.parse(JSON.stringify())
  • lodash的深拷贝函数

《Android最全面试题-Offer直通车》目录 如今Android求职是僧多粥少,竞争非常激烈,企业招聘时要求很高,本专栏汇聚android面试各种考点,各大公司Android面试题,《Android 最全面试题》将助你全方位提升,助您脱颖而出,早日拿到满意Offer。本专栏汇聚了海量优质内容,文章数量超过100 篇。凭借过硬的质量,每月新增订阅用户数以百计,如今仅需特惠价 9.9 元,限时抢购,后续将逐步恢复原价,早订阅早开启逆袭之旅。精心筹备,整理出一系列超万字精华篇章,累计数十万的 Android 面试题深度剖析。 阅读详情

相关推荐

ddr3ip核心_DDR3 IP 核配置

DDR3 控制器 IP 核2.DDR3 IP核配置点击 Flow Navigator 面板下的“Project Manager -->IP Catalog”。 在“Memories & Storage Elements -->Memory Interface Generators”分类展开后,可以看到名为“Memory Interface Generator (MIG 7 ...

weixin_33049919的博客 2935

JavaScript 中如何实现对象的 deep clone

浅克隆 在 javaScript 中 = 和 Object.assign() 包括 ES6 的 spread operator 都是浅克隆,= 只会对原对象进行引用,后两种方法只会对对象的第一层进行克隆。 深度克隆 深度克隆指生成一个独立的对象,和原对象之间不会互相影响。 对一个对象深度克隆,完全取决于你所要拷贝的数据类型。 let obj = { a: 1, b: { ...

weixin_34049948的博客 279

C语言的文件读取与写入操作

学了一年的C语言了,现在回过头来做一下总结。并且博客开了挺长一段时间却没有谢什么实际的东西。现在做下总结,对之后的学习帮助应该挺大的。与大家共勉! 现在,我先来介绍一下C语言的文件读取与写入的原理和具体操作方法。 C语言文件读取与写入是通过将文件看成一个字符序列进行读入和写出的。所以读取与写入文件就用到了指针,而这个指针是一个特殊的指针,我们称为文件指针。 指针名称是

wzyxdwll的博客 3万+

JS深拷贝的实现

原文再续,书接上一回(【JavaScript笔记06】赋值、浅拷贝、深拷贝),在这篇文章实现一下深拷贝的方法。 首先来个最基本的拷贝,创建一个新对象,遍历需要克隆的对象,将需要克隆对象的属性依次添加到新对象上即可,但是面对多层的对象就不行了。 let obj1 = { name:'jjy', age:23, child:{ id:4 } } function t1(source){ let cloneTarget = {} for(const

JJY的博客 603

每日算法讲解(十):使用BP神经网络进行回归任务

BP神经网络是由输入层、隐藏层和输出层组成的多层前馈神经网络,通过反向传播算法(Backpropagation)来调整网络权重和偏置,以最小化预测误差。前向传播:将输入数据通过网络,计算每层的激活值和输出。计算误差:根据预测值和真实值之间的差异计算损失函数值。反向传播:从输出层开始,逐层向后计算梯度,并更新每层的权重和偏置。重复迭代:通过多次迭代,逐步减小预测误差,优化模型参数。接下来,我们定义一个包含一个输入层、两个隐藏层和一个输出层的BP神经网络模型。# 定义模型# 编译模型。

rhtee的博客 1728

用友U8 API 二次开发:WebService封装7个核心DLL引用与U8Login登录避坑

本文详细介绍了用友U8 API二次开发中的WebService封装与U8Login登录优化实践。通过7个核心DLL的正确引用、U8Login登录机制的深度解析及IIS环境下的特殊处理方案,为开发者提供了一套高可用性的通用解决方案,助力企业实现ERP系统的高效集成与数字化转型。

weixin_42524171的博客 505

JS深拷贝(deepClone)比较实用的两种方法

分享下js深拷贝对象,两种最常用、最实用的方法 方法1、递归遍历对象的属性,返回一个新的对象 // 这里有两种写法,都可用 const obj1 = { name: 'wang', f: function() {}, tar: { name:'Jason', age: 18 } } function deepClone(obj) { // 写法1 // let newObj = obj.constructor === Array ? [] : {} // if(typeof obj !=

weixin_46482956的博客 4358

第一章 初识SpringBoot

提供大量的自动配置Spring和第三方库,开发者可以根据项目需求选择相关的starters依赖,而无需手动配置大量的Spring配置文件,开箱即用,无需配置XML,无代码生成。Spring Boot与Spring Cloud等框架集成,为构建微服务提供了强大的支持,并通过Spring Cloud实现服务注册、负载均衡、熔断等功能。本质上讲,Spring Boot就是Spring,它做了那些本应我们去做的Spring Bean配置等工作。以上就是一个简单的Spring Boot应用的创建和运行过程。

Calvin的博客 1581

JS 使用cloneChild 克隆元素节点

<html test 克隆 橘子 草莓 哈密瓜 苹果 葡萄 猕猴桃 var btn4Obj = document.getElementById('btn4'); var ulObj = document.getElementsByClassName('ul'); var lis = document.getEl

又是再见? 1万+

基于Qt C++开发一个体测数据管理系统

advice.dietAdvice = "饮食建议:\n1. 控制总热量摄入,每天减少300-500大卡\n2. 减少精制碳水(白米饭、白面包、甜食)\n3. 增加优质蛋白和膳食纤维摄入\n4. 烹饪方式以蒸、煮、烤、凉拌为主,减少油炸\n5. 晚餐适量减少,睡前3小时避免进食";dataTable->setHorizontalHeaderLabels({"ID", "用户名", "身高(cm)", "体重(kg)", "BMI", "体脂率(%)", "记录时间"});

寒寒豆豆软件公司的博客 192

CMake入门教程【基础篇】CMake+Visual Studio2022构建C++项目

CMake+Visual Studio2022搭建C++环境

小鱼酱的博客 4971

JS 浅拷贝和深拷贝详解(巨详细)

浅拷贝,指的是创建新的数据,这个数据有着原始数据属性值的一份精确拷贝如果属性是基本类型,拷贝的就是基本类型的值。如果属性是引用类型,拷贝的就是内存地址即浅拷贝是拷贝一层,深层次的引用类型则共享内存地址深拷贝开辟一个新的栈,两个对象属性完成相同,但是对应两个不同的地址,修改一个对象的属性,不会改变另一个对象的属性。

qq_63299825的博客 5520

如何使用PostgreSQL数据库进行数据仓库的ETL操作

本文旨在为数据工程师、数据库管理员和分析师提供使用PostgreSQL进行数据仓库ETL操作的全面指南。ETL流程的基本原理PostgreSQL在ETL中的独特优势实际ETL实现的技术细节性能优化和最佳实践常见问题的解决方案首先介绍ETL和PostgreSQL的基本概念然后深入探讨核心技术和实现方法接着提供实际案例和代码示例最后讨论优化技巧和未来趋势ETL:提取(Extract)、转换(Transform)、加载(Load)的缩写,描述数据从源系统移动到目标数据仓库的过程。

2502_91592937的博客 873

js数组深拷贝_秒懂JavaScript中的深浅拷贝

1、从原理上搞懂深浅拷贝掌握深浅拷贝之前,我们需要简单的介绍一下js中的数据类型。js中的数据类型分为基本数据类型和引用数据类型。基本数据类型是按值操作的,而引用数据类型操作的是其在堆内存中的地址。我大概举个例子:基础数据类型就好像图书馆的每一本书(文史,经济,艺术等这些我们可以对应基础数据类型中的number,string,boolean等),引用数据类型就好像是图书馆里的书籍索引(一般的书籍索...

weixin_39956182的博客 178

TMS_WebGMaps_1.9.8.0_Full_Source

TMS_WebGMaps_1.9.8.0_Full_Source 全源码 delphi cbuilder

RU DOS版、EFI版32bit/64bit BIOS常用工具

DOS版、EFI版的RU工具,可用来查看IO、MEM、PCI、SMBIOS等资源。亲测在EFI SHELL下可用。

js高级深浅拷贝

原理:用JSON.stringify将对象转成JSON字符串,再用JSON.parse()把字符串解析成对象,一去一来,新的对象产生了,而且对象会开辟新的栈,实现深拷贝。当对象中只有一级属性,没有二级属性的时候,此方法为深拷贝,但是对象中有对象的时候,此方法,在二级属性以后就是浅拷贝。深浅拷贝主要区别在复杂数据类型,对于基本数据类型,没有区别,改变拷贝的数据,都不会改变原数据。浅拷贝只拷贝引用(地址值),当拷贝的新对象发生改变时,原对象也会发生相同的改变,也就是说,:目标对象,即接收源对象属性的对象。

奋斗的小前端 1211

ssm框架搭建

这个是一个在idea上成功跑起来的ssm框架项目,保存在这里方便以后使用直接下载

JavaScript:深浅拷贝

了解深浅拷贝之前必须要先知道js数据类型和存储方式(基本数据类型储存在栈内存,引用类型储存在堆内存中)。

weixin_49549489的博客 150

华为的C语言编程规范.pdf

华为技术有限公司内部技术规范。本规范制定了编写C语言程序的基本原则、规则和建议。从代码的清晰、简洁、可测试、安全、程序效率、可移植各个方面对C语言编程作出了具体指导。

Javascript中的深浅拷贝以及实现方法

Javascript中的深浅拷贝以及实现方法

weixin_74457498的博客 1990

JavaScript之深浅拷贝

上篇博客中简单介绍了JS中的引用类型,本篇博客就简单介绍下JS的深浅拷贝,以后有时间再对深浅拷贝做个扩展。原始类型是保存在栈内存中的,对于它们的复制可以理解为“真实”复制,即重新开辟栈内存并将原来的值copy一份放进去。那么修改copy的那个值并不会影响原始值,因为它们是独立的。 先看看原始类型和引用类型的存储方式:(图片摘自https://www.cnbl...

feb的博客 466

JavaScript深浅拷贝

在前端开发中,有时我们需要对一个复杂的数据结构进行变换,例如将一个嵌套的对象转换为数组或将一个数组转换为嵌套的对象。深拷贝是对对象或数组进行递归复制,复制整个对象的所有层次,这样就可以避免对象中的引用类型的属性共享的问题。浅拷贝是对对象或数组进行浅层复制,只复制对象的一层属性,当复制的对象中有引用类型的属性时,复制出来的对象和原对象会共享引用类型的属性。通过展开一个对象,可以将其属性复制到新的对象中。总之,在前端开发中,深拷贝是非常常见的场景,可以帮助我们处理复杂的数据结构,并保持代码的稳定性和可维护性。

码农键盘上的梦 722

js深浅拷贝的理解

js深浅拷贝的理解 说起js的深浅拷贝不得不提起的是js的数据类型 基本数据类型 :undfined string boolean null number 引用数据类型:array object function 基本数据类型存储在栈内存中 变量 值 a 1(栈内存) b (指针) var a=1;var b = a a=3;console.log(a,b)//3,1 ...

weixin_42446516的博客 431

js数组深拷贝_如何快速理解javascript中的深浅拷贝?

最近刷掘金看到axuebin大佬的2020年前端面试复习必读文章【超百篇文章/赠复习导图】 - 掘金,让我受益匪浅。所以决定开始写一些基础文档,提升一下自己,顺便回顾一下原生JS.如果要彻底理解JavaScript的深浅拷贝,必须要知道javascript的数据类型医技JS中数据是如何存储的,下面我逐一介绍:基本数据类型javascript 中的数据类型分为基础数据类型和引用数据类型基础数据类型指...

weixin_39987211的博客 124

js高级04-深浅拷贝、防抖节流

举例:比如我们在玩LOL游戏时,当需要回城时,我们触发回城按钮,进入到回城状态进会有一个等待的时间,如果在这个等待时间内有重复去执行回城按钮,这时回城状态并不会做出其他响应,而是等时间到了后才会完成回城。每一级的数据都会拷贝 ,拷贝后,两个对象拥有不同的地址,当拷贝出来的对象发生改变时,原对象内容不会改变,两者互不影响。浅拷贝只拷贝引用(地址值),当拷贝的新对象发生改变时,原对象也会发生相同的改变,也就是说,浅拷贝会影响原来的元素。引用数据类型的特点:存储的是该对象在栈中引用,真实的数据存放在堆内存里。

qq_60060362的博客 969

JS专题之深浅拷贝

前言 在开发过程中,偶尔会遇到这种场景,拿到一个数据后,你打算对它进行处理,但是你又希望拷贝一份副本出来,方便数据对比和以后恢复数据。 那么这就涉及到了 JS 中对数据的深浅拷贝问题,所谓深浅拷贝,浅拷贝的意思就是,你只是复制了对象数据的引用,并没有把内存里的值另外复制一份,那么深拷贝就是把值完整地复制一份新的值。 在之前的文章《JS专题之数据类型和类型检测》中我有讲过,JS 中的数据类型分为两种...

weixin_34293902的博客 244

js 浅拷贝直接赋值_如何快速理解javascript中的深浅拷贝?

最近刷掘金看到axuebin大佬的2020年前端面试复习必读文章【超百篇文章/赠复习导图】 - 掘金,让我受益匪浅。所以决定开始写一些基础文档,提升一下自己,顺便回顾一下原生JS.如果要彻底理解JavaScript的深浅拷贝,必须要知道javascript的数据类型医技JS中数据是如何存储的,下面我逐一介绍:基本数据类型javascript 中的数据类型分为基础数据类型和引用数据类型基础数据类型指...

weixin_39752828的博客 121

JS知识点总结二———深浅拷贝

本文深入解析了JavaScript中的深拷贝与浅拷贝。浅拷贝仅复制对象的第一层属性,当属性为引用类型时复制的是内存地址(如Object.assign、扩展运算符等方法);深拷贝则递归复制所有层级,创建完全独立的对象(如JSON.parse/stringify、递归实现等)。文章详细对比了两种拷贝方式的实现原理、典型应用场景及各自的局限性,并提供了包含循环引用处理、类型识别和原型链保留的完整深拷贝解决方案。通过掌握这些核心概念,开发者可以更好地处理数据引用问题,避免对象间的意外关联。

不会后端的前端不是一个好全栈 2098

JavaScript——23 深浅拷贝

讲解了JS的深浅拷贝,其中深拷贝用了三种方式实现,包括通过递归实现、cloneDeep方法实现和JSON.stringify()实现。

念头通达 851
上一篇: js 数据类型、类型转换
下一篇: JS 参数传递
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值