JS深浅拷贝及其实现

基本数据类型和引用数据类型
JS数据分为基本数据类型和引用数据类型。基本数据类型的变量存储在栈中,引用数据类型则存储在堆中,引用数据类型的存储地址则保存在栈中。
下面来看一个小例子
// 基本数据类型
let intType = 1;
console.log('初始intType:' + intType);
let copyIntType = intType;
copyIntType = 2;
console.log('更改后intType:' + intType);
console.log('更改后copyIntType:' + intType);
let object = {
a: 1
};
// 引用数据类型
let copyObject = object
console.log('初始object:');
console.log(object);
copyObject.a = 2;
console.log('更改后的object:');
console.log(object);
console.log('更改后的copyObject:');
console.log(copyObject);
结果:
基本数据类型在复制的时候会创建一个值的副本,并将该副本赋值给新变量。引用类型在复制的时候其实复制的是指针。
深浅拷贝
- 浅拷贝:仅仅是复制了引用,彼此之间的操作会互相影响
- 深拷贝:在堆中重新分配内存,不同的地址,相同的值,互不影响
浅拷贝
创建一个新对象,这个对象有着原始对象属性值的一份精确拷贝。如果属性是基本类型,拷贝的就是基本类型的值,如果属性是引用类型,拷贝的就是内存地址 ,所以如果其中一个对象改变了这个地址,就会影响到另一个对象。实现方式如下
实现
- 遍历赋值实现
- ES6扩展运算符
- ES6方法Object.assign()
- 数组方法(只适用于类数组对象)
Array.from(arrayLike)
Array.prototype.concat()
Array.prototype.slice()
遍历赋值实现
var obj = { a:1, arr: [2,3] };
//浅拷贝实现
for (var prop in obj){
if(obj.hasOwnProperty(prop)){
shallowObj[prop] = obj[prop];
}
}
ES6扩展运算符
var obj = { a:1, arr: [2,3] };
var obj1 = {...obj}
ES6方法Object.assign()
var obj = { a:1, arr: [2,3] };
var obj1 = Object.assign({}, obj);
数组方法(仅适用于类数组对象)
Array.from(arrayLike)
var array1 = ['a', ['b', 'c'], 'd'];
var array2 = Array.from(array1);
Array.prototype.concat()
var array1 = ['a', ['b', 'c'], 'd'];
var array2 = array1.concat([1,2]);
Array.prototype.slice()
var array1 = ['a', ['b', 'c'], 'd'];
var array2 = array1.slice(0,2);
引用赋值:地址的赋值,将对象指针赋值给一个变量,让此变量指向对象
深拷贝
将一个对象从内存中完整的拷贝一份出来,从堆内存中开辟一个新的区域存放新对象,且修改新对象不会影响原对象
实现方式如下:
- JSON.parse()和JSON.stringify()
- 递归
JSON.parse()和JSON.stringify()
let parseObject = {
a: {
b: 1
}
}
let cloneParseObject = JSON.parse(JSON.stringify(parseObject));
parseObject.a.b = 2;
console.log('cloneParseObject');
console.log(cloneParseObject);
缺陷:
- 会忽略 undefined
- 会忽略 symbol
- 无法对function进行处理 需要确认.
- 不能解决循环引用的对象
递归(简单版)
// 深拷贝
function cloneDeep(obj) {
if (!obj && typeof obj !== 'object') {
throw new Error('错误参数');
}
const targetObj = Array.isArray(obj) ? [] : {};
for (let key in obj) {
//只对对象自有属性进行拷贝
if (obj.hasOwnProperty(key)) {
if (obj[key] && typeof obj[key] === 'object') {
targetObj[key] = cloneDeep(obj[key]);
} else {
targetObj[key] = obj[key];
}
}
}
return targetObj;
}
关键点
- 判断参数类型
- 判断是否是数组
- for in遍历
- 判断是否是自有对象
- 判断子属性是否是对象,是对象则递归
递归(复杂版)
const mapTag = '[object Map]';
const setTag = '[object Set]';
const arrayTag = '[object Array]';
const objectTag = '[object Object]';
const argsTag = '[object Arguments]'; const boolTag = '[object Boolean]';
const dateTag = '[object Date]';
const numberTag = '[object Number]';
const stringTag = '[object String]';
const symbolTag = '[object Symbol]';
const errorTag = '[object Error]';
const regexpTag = '[object RegExp]';
const funcTag = '[object Function]'; const deepTag = [mapTag, setTag, arrayTag, objectTag, argsTag]; function forEach(array, iteratee) {
let index = -1;
const length = array.length;
while (++index < length) {
iteratee(array[index], index);
}
return array;
} function isObject(target) {
const type = typeof target;
return target !== null && (type === 'object' || type === 'function');
} function getType(target) {
return Object.prototype.toString.call(target);
} function getInit(target) {
const Ctor = target.constructor;
return new Ctor();
} function cloneSymbol(targe) {
return Object(Symbol.prototype.valueOf.call(targe));
} function cloneReg(targe) {
const reFlags = /\w*$/;
const result = new targe.constructor(targe.source, reFlags.exec(targe));
result.lastIndex = targe.lastIndex;
return result;
} function cloneFunction(func) {
const bodyReg = /(?<={)(.|\n)+(?=})/m;
const paramReg = /(?<=\().+(?=\)\s+{)/;
const funcString = func.toString();
if (func.prototype) {
const param = paramReg.exec(funcString);
const body = bodyReg.exec(funcString);
if (body) {
if (param) {
const paramArr = param[0].split(',');
return new Function(...paramArr, body[0]);
} else {
return new Function(body[0]);
}
} else {
return null;
}
} else {
return eval(funcString);
}
} function cloneOtherType(targe, type) {
const Ctor = targe.constructor;
switch (type) {
case boolTag:
case numberTag:
case stringTag:
case errorTag:
case dateTag:
return new Ctor(targe);
case regexpTag:
return cloneReg(targe);
case symbolTag:
return cloneSymbol(targe);
case funcTag:
return cloneFunction(targe);
default:
return null;
}
}
function clone(target, map = new WeakMap()) {
// 克隆原始类型
if (!isObject(target)) {
return target;
}
// 初始化
const type = getType(target);
let cloneTarget;
if (deepTag.includes(type)) {
cloneTarget = getInit(target, type);
} else {
return cloneOtherType(target, type);
}
// 防止循环引用
if (map.get(target)) {
return map.get(target);
}
map.set(target, cloneTarget);
// 克隆set
if (type === setTag) {
target.forEach(value => {
cloneTarget.add(clone(value, map));
});
return cloneTarget;
}
// 克隆map
if (type === mapTag) {
target.forEach((value, key) => {
cloneTarget.set(key, clone(value, map));
});
return cloneTarget;
}
// 克隆对象和数组
const keys = type === arrayTag ? undefined : Object.keys(target);
forEach(keys || target, (value, key) => {
if (keys) {
key = value;
}
cloneTarget[key] = clone(target[key], map);
});
return cloneTarget;
}
module.exports = {
clone
};
示例代码
参考
JS深浅拷贝及其实现的更多相关文章
- js 深浅拷贝 笔记总结
一.js 数据类型 javaScritp的数据类型有:数值类型.字符串类型.布尔类型.null.undefined.对象(数组.正则表达式.日期.函数),大致分成两种:基本数据类型和引用数据类型, 其 ...
- jQuery开发插件的两个方法 js 深浅拷贝
1.jQuery.extend(object);为扩展jQuery类本身.为类添加新的方法.由全局函数来调用, 主要是用来拓展个全局函数 2.jQuery.fn.extend(object);为jQu ...
- js深浅拷贝
作为一枚前段,我们知道对象类型在赋值的过程中其实是复制了地址,从而会导致改变了一方其他也都被改变的情况.通常在开发中我们不希望出现这样的问题,我们可以使用浅拷贝来解决这个情况. 浅拷贝 首先可以通过O ...
- 最简js深浅拷贝说明
1.浅拷贝 浅拷贝是拷贝引用,拷贝后的引用都是指向同一个对象的实例,彼此之间的操作会互相影响. 浅拷贝分两种情况: 1.直接拷贝源对象的引用 2. 源对象拷贝实例,但其属性对象(类型为Object, ...
- JS 深浅拷贝
首先理解概念 浅拷贝: 只复制对象的基本类型, 对象类型, 仍属于原来的引用. 深拷贝: 不紧复制对象的基本类, 同时也复制原对象中的对象.就是说完全是新对象产生的. 首先看浅拷贝 //浅拷贝 var ...
- JS中深浅拷贝 函数封装代码
一.了解 基本数据类型保存在栈内存中,按值访问,引用数据类型保存在堆内存中,按址访问. 二.浅拷贝 浅拷贝只是复制了指向某个对象的指针,而不是复制对象本身,新旧对象其实是同一内存地址的数据,修改其中一 ...
- js 基础数据类型和引用类型 ,深浅拷贝问题,以及内存分配问题
js 深浅拷贝问题 浅拷贝一般指的是基本类型的复制 深拷贝一般指引用类型的拷贝,把引用类型的值也拷贝出来 举例 h5的sessionStorage只能存放字符串,所以要存储json时就要把json使用 ...
- JS--变量及深浅拷贝
JS变量分为基本类型和引用类型 基本类型数据包括Number, String, Boolean, Null, Undefined五种类型: 引用数据类型包括Array, Date, RegExp, F ...
- 【 js 基础 】 深浅拷贝
underscore的源码中,有很多地方用到了 Array.prototype.slice() 方法,但是并没有传参,实际上只是为了返回数组的副本,例如 underscore 中 clone 的方法: ...
随机推荐
- 唯一约束 UNIQUE KEY
目录 什么是唯一约束 与主键的区别 创建唯一约束 唯一性验证 什么是唯一约束 Unique Key:它是 MySQL 中的唯一约束,是指在所有记录中字段的值不能重复出现.例如,为 id 字段加上唯一性 ...
- Asp.Net项目发布 到 IIS、 Core3.1 发布到 IIS CentOS8.x
摘要:发布项目到IIS或者.Net Core 项目发布到IIS服务器或者CentOS记录一下,后面忘了又来看看. 1.服务器安装IIS 1.1.不管你是本地的电脑还是网上购买的服务器,只要是能通过远程 ...
- Spring事务专题(三)事务的基本概念,Mysql事务处理原理
前言 本专题大纲: 我重新整理了大纲,思考了很久,决定单独将MySQL的事务实现原理跟Spring中的事务示例分为两篇文章,因为二者毕竟没有什么实际关系,实际上如果你对MySQL的事务原理不感兴趣也可 ...
- 服务消费者(RestTemplate+Ribbon+feign)
负载均衡 spring cloud 体系中,我们知道服务之间的调用是通过http协议进行调用的.注册中心就是维护这些调用的服务的各个服务列表.在Spring中提供了RestTemplate,用于访 ...
- 【NOI2016】区间 题解(线段树+尺取法)
题目链接 题目大意:给定$n$个区间$[l_i,r_i]$,选出$m$个区间使它们有一个共同的位置$x$,且使它们产生的费用最小.求最小费用.费用定义为最长的区间长度减去最短区间长度. ------- ...
- Docker技术入门与实战第2版-高清文字版
Docker技术入门与实战第2版-高清文字版 下载地址https://pan.baidu.com/s/1bAoRQQlvBa-PXy5lgIlxUg 扫码下面二维码关注公众号回复100011 获取 ...
- IDEA中配置Maven
目录 一.IDEA 集成 Maven 二.IDEA 创建 Maven 版 java 工程 1. 创建 maven 版 java 工程 2. 填写 maven 工程的坐标 3. 填写工程名和存储路径 4 ...
- SSH config语法关键字
1.SSH config 语法关键字 host 别名 HostName 主机名(ip) User 用户(root就是一个用户) Port 端口(默认22) IdentityFile 密钥文件的路 ...
- 【模式识别与机器学习】——logistic regression
虽然叫做“回归”,但是这个算法是用来解决分类问题的.回归与分类的区别在于:回归所预测的目标量的取值是连续的(例如房屋的价格):而分类所预测的目标变量的取值是离散的(例如判断邮件是否为垃圾邮件).当然, ...
- JS学习第一天
JS的三种引入方式: 内联:在标签属性中引入javascript:js代码 <a href="javascript:("helloworld")"& ...