浅拷贝

浅拷贝是拷贝第一层的拷贝

使用Object.assign解决这个问题。

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

深拷贝

简单的做法:JSON.parse(JSON.stringify(obj))

但是该方法也是有局限性的:

  • 会忽略undefined
  • 会忽略symbol
  • 会忽略函数
  • 不能解决循环引用的对象 (会抱错)

如果你所需拷贝的对象含有内置类型并且不包含函数,可以使用 MessageChannel

自封装深拷贝

思路:

  1. 使用for-in遍历对象
  2. 因为for-in会遍历原型链上的属性,所以需要判断属性是否在原型链上,不是原型链才拷贝
  3. 判断属性值类型是原始类型和引用类型
  4. 原始类型直接赋值(注意null)
  5. 引用类型判断是对象还是数组,创建对应的空对象或空数组,递归调用函数,将值赋值进去
/**
* 深度克隆
* @param origin 被拷贝的原对象
* @param target 拷贝出来的对象
* @return 拷贝出来的对象
*/
function deepClone(origin, target) {
if(typeof(origin) !== 'object' || origin) {
return origin;
}
target = target || {}; for(let prop in origin) { //使用 for-in
if(origin.hasOwnProperty(prop)) { //不是原型链上的
if(typeof(origin[prop]) === 'object' && origin[prop] ) { //是对象
// 先判断是不是数组
if(origin[prop] instanceof Array) {
target[prop] = [];
deepClone(origin[prop], target[prop]);
} else {
target[prop] = {};
deepClone(origin[prop], target[prop]);
}
}
else {
target[prop] = origin[prop];
}
}
}
return target;
}

深拷贝 - BFS

// 如果是对象/数组,返回一个空的对象/数组,
// 都不是的话直接返回原对象
function getEmptyArrOrObj(item) {
let itemType = Object.prototype.toString.call(item)
if(itemType === '[object Array]') {
return []
}
if(itemType === '[object Object]') {
return {}
}
return item
} function deepCopyBFS(origin) {
const queue = []
const map = new Map() // 记录出现过的对象,处理环 let target = getEmptyArrOrObj(origin) if(target !== origin) {
// 说明origin是一个对象或数组,需要拷贝子代
queue.push([origin, target]);
map.set(origin, target)
} while(queue.length) { let [ori, tar] = queue.shift(); // 出队 for(let key in ori) {
if(ori.hasOwnProperty(key)) { // 不在原型上 if(map.get(ori[key])) { // 处理环
tar[key] = map.get(ori[key])
continue
} tar[key] = getEmptyArrOrObj(ori[key]);
if(tar[key] !== ori[key]) {
queue.push(ori[key], tar[key])
map.set(ori[key], tar[key])
}
}
}
} return target
}

深拷贝 - DFS

function deepCopyDFS(origin){
let stack = [];
let map = new Map(); // 记录出现过的对象,用于处理环 let target = getEmptyArrOrObj(origin);
if(target !== origin){
stack.push([origin, target]);
map.set(origin, target);
} while(stack.length){
let [ori, tar] = stack.pop();
for(let key in ori){
if(ori.hasOwnProperty(key)) { // 不在原型上
// 处理环状
if(map.get(ori[key])){
tar[key] = map.get(ori[key]);
continue;
} tar[key] = getEmptyArrOrObj(ori[key]);
if(tar[key] !== ori[key]){
stack.push([ori[key], tar[key]]);
map.set(ori[key], tar[key]);
}
}
}
} return target;
}

测试上面的两个 deepCopy

JS中的深拷贝和浅拷贝的更多相关文章

  1. js 中的深拷贝与浅拷贝

    在面试中经常会问到js的深拷贝和浅拷贝,也常常让我们手写,下面我们彻底搞懂js的深拷贝与浅拷贝. 在js中 Array 和 Object  这种引用类型的值,当把一个变量赋值给另一个变量时,这个值得副 ...

  2. js中的深拷贝与浅拷贝

    对象的深拷贝于浅拷贝 对于基本类型,浅拷贝过程就是对值的复制,这个过程会开辟出一个新的内存空间,将值复制到新的内存空间.而对于引用类型来书,浅拷贝过程就是对指针的复制,这个过程并没有开辟新的堆内存空间 ...

  3. js 中的 深拷贝与浅拷贝

    js在平时的项目中,赋值操作是最多的:比如说: var person1 = { name:"张三", age:18, sex:"male", height:18 ...

  4. js中的深拷贝和浅拷贝2

    所谓 深浅拷贝: 对于仅仅是复制了引用(地址),换句话说,复制了之后,原来的变量和新的变量指向同一个东西,彼此之间的操作会互相影响,为 浅拷贝. 而如果是在堆中重新分配内存,拥有不同的地址,但是值是一 ...

  5. JS对象复制(深拷贝、浅拷贝)

    如何在 JS 中复制对象 在本文中,我们将从浅拷贝(shallow copy)和深拷贝(deep copy)两个方面,介绍多种 JS 中复制对象的方法. 在开始之前,有一些基础知识值得一提:Javas ...

  6. javascript中的深拷贝与浅拷贝

    javascript中的深拷贝与浅拷贝 基础概念 在了解深拷贝与浅拷贝的时候需要先了解一些基础知识 核心知识点之 堆与栈 栈(stack)为自动分配的内存空间,它由系统自动释放: 堆(heap)则是动 ...

  7. 浅谈Java中的深拷贝和浅拷贝(转载)

    浅谈Java中的深拷贝和浅拷贝(转载) 原文链接: http://blog.csdn.net/tounaobun/article/details/8491392 假如说你想复制一个简单变量.很简单: ...

  8. C语言中的深拷贝和浅拷贝

    //C语言中的深拷贝和浅拷贝 #define _CRT_SECURE_NO_WARNINGS #include<stdio.h> #include<stdlib.h> #inc ...

  9. 浅谈Java中的深拷贝和浅拷贝

    转载: 浅谈Java中的深拷贝和浅拷贝 假如说你想复制一个简单变量.很简单: int apples = 5; int pears = apples; 不仅仅是int类型,其它七种原始数据类型(bool ...

随机推荐

  1. nyoj 113-字符串替换 (python replace, try ... except)

    113-字符串替换 内存限制:64MB 时间限制:3000ms 特判: No 通过数:31 提交数:71 难度:2 题目描述: 编写一个程序实现将字符串中的所有"you"替换成&q ...

  2. 领扣(LeetCode)检测大写字母 个人题解

    给定一个单词,你需要判断单词的大写使用是否正确. 我们定义,在以下情况时,单词的大写用法是正确的: 全部字母都是大写,比如"USA". 单词中所有字母都不是大写,比如"l ...

  3. 用安全密钥验证ssh

    1.需要打开两台虚拟机,并保证两台虚拟机可以ping通: 本地主机IP:192.168.8.120 远程主机IP:192.168.8.100 2.在本地主机生成密钥对,输入命令“ssh-keygen” ...

  4. canvas入门,就是这个feel!

    钙素 Canvas 是在HTML5中新增的标签用于在网页实时生成图像,并且可以操作图像内容,基本上它是一个可以用JavaScript操作的位图.也就是说我们将通过JS完成画图而不是css. canva ...

  5. 2019-10-29:渗透测试,基础学习,sqlmap文件读取,写入,dnslog盲注作用,mssql手工注入,笔记

    sqlmap参数--file-read,从数据库服务器中读取文件--file-write,--file-dest,把文件上传到数据库服务器中 dnslog平台的学习和它在盲注中的应用1,判断注入点2, ...

  6. HTML字符实体(Character Entities)与 转义字符串(Escape Sequence)(转)

    为什么要用转义字符串?HTML中<,>,&等有特殊含义(<,>,用于链接签,&用于转义),不能直接使用.这些符号是不显示在我们最终看到的网页里的,那如果我们希望 ...

  7. PHP基于Redis实现轻量级延迟队列

    延迟队列,顾名思义它是一种带有延迟功能的消息队列. 那么,是在什么场景下我才需要这样的队列呢? 一.背景 先看看一下业务场景: 1.会员过期前3天发送召回通知 2.订单支付成功后,5分钟后检测下游环节 ...

  8. Java常见网络操作(URL类,InetAddress类,URLConnection类)

    *****************InetAddress********************** InetAddress:用于标识网络上的硬件资源(如,IP,主机名,域名等).    对于Inet ...

  9. C语言之修改常量

    前言:指针!菜鸟的终点,高手的起点.漫谈一些进阶之路上的趣事:记录一些语言本身的特性以及思想,没有STL,也没有API! 0x01: 程序内存中的存储划分 对于程序在内存中是如何分布的,网上有多个解释 ...

  10. 【Android - IPC】之AIDL简介

    参考资料: 1.<Android开发艺术探索>第二章2.4.4 2.Android AIDL Binder框架解析:http://blog.csdn.net/lmj623565791/ar ...