[转] js深拷贝和浅拷贝
一、数组的深浅拷贝
在使用JavaScript对数组进行操作的时候,我们经常需要将数组进行备份,事实证明如果只是简单的将它赋予其他变量,那么我们只要更改其中的任何一个,然后其他的也会跟着改变,这就导致了问题的发生。
var arr = ["One","Two","Three"]; var arrto = arr;
arrto[1] = "test";
document.writeln("数组的原始值:" + arr + "<br />");//Export:数组的原始值:One,test,Three
document.writeln("数组的新值:" + arrto + "<br />");//Export:数组的新值:One,test,Three
像上面的这种直接赋值的方式就是浅拷贝,很多时候,这样并不是我们想要得到的结果,其实我们想要的是arr的值不变,不是吗?
方法一:js的slice函数

对于array对象的slice函数,
返回一个数组的一段。(仍为数组)
arrayObj.slice(start, [end])
参数
arrayObj
必选项。一个 Array 对象。
start
必选项。arrayObj 中所指定的部分的开始元素是从零开始计算的下标。
end
可选项。arrayObj 中所指定的部分的结束元素是从零开始计算的下标。
说明
slice 方法返回一个 Array 对象,其中包含了 arrayObj 的指定部分。
slice 方法一直复制到 end 所指定的元素,但是不包括该元素。如果 start 为负,将它作为 length + start处理,此处 length 为数组的长度。如果 end 为负,就将它作为 length + end 处理,此处 length 为数组的长度。如果省略 end ,那么 slice 方法将一直复制到 arrayObj 的结尾。如果 end 出现在 start 之前,不复制任何元素到新数组中。

var arr = ["One","Two","Three"]; var arrtoo = arr.slice(0);
arrtoo[1] = "set Map";
document.writeln("数组的原始值:" + arr + "<br />");//Export:数组的原始值:One,Two,Three
document.writeln("数组的新值:" + arrtoo + "<br />");//Export:数组的新值:One,set Map,Three
方法二:js的concat方法
concat() 方法用于连接两个或多个数组。
该方法不会改变现有的数组,而仅仅会返回被连接数组的一个副本。
语法
arrayObject.concat(arrayX,arrayX,......,arrayX)
说明
返回一个新的数组。该数组是通过把所有 arrayX 参数添加到 arrayObject 中生成的。如果要进行 concat() 操作的参数是数组,那么添加的是数组中的元素,而不是数组。
var arr = ["One","Two","Three"]; var arrtooo = arr.concat();
arrtooo[1] = "set Map To";
document.writeln("数组的原始值:" + arr + "<br />");//Export:数组的原始值:One,Two,Three
document.writeln("数组的新值:" + arrtooo + "<br />");//Export:数组的新值:One,set Map To,Three
二、对象的深浅拷贝
var a={name:'yy',age:26};
var b=new Object();
b.name=a.name;
b.age=a.age;
a.name='xx';
console.log(b);//Object { name="yy", age=26}
console.log(a);//Object { name="xx", age=26}
就是把对象的属性遍历一遍,赋给一个新的对象。
var deepCopy= function(source) {
var result={};
for (var key in source) {
      result[key] = typeof source[key]===’object’? deepCoyp(source[key]): source[key];
   }
   return result;
}
2016-11-26
---------------------------------------------------------------------------------------------------------------------------------------------------
2019-09-26
深克隆方法
方法一
const newObj = JSON.parse(JSON.stringify(oldObj));
这个方法虽然可以解决绝大部分是使用场景,但是却有很多坑。
他无法实现对函数 、RegExp等特殊对象的克隆
会抛弃对象的constructor,所有的构造函数会指向Object
对象有循环引用,会报错
// 构造函数
function person(pname) {
this.name = pname;
} const Messi = new person('Messi');
// 函数
function say() {
console.log('hi');
}; const oldObj = {
a: say,
b: new Array(1),
c: new RegExp('ab+c', 'i'),
d: Messi
}; const newObj = JSON.parse(JSON.stringify(oldObj)); // 无法复制函数
console.log(newObj.a, oldObj.a); // undefined [Function: say]
// 稀疏数组复制错误
console.log(newObj.b[0], oldObj.b[0]); // null undefined
// 无法复制正则对象
console.log(newObj.c, oldObj.c); // {} /ab+c/i
// 构造函数指向错误
console.log(newObj.d.constructor, oldObj.d.constructor); // [Function: Object] [Function: person]
面试版深克隆
/**
* deep clone
* @param {[type]} parent object 需要进行克隆的对象
* @return {[type]} 深克隆后的对象
*/
const clone = parent => {
// 判断类型
const isType = (obj, type) => {
if (typeof obj !== "object") return false;
const typeString = Object.prototype.toString.call(obj);
let flag;
switch (type) {
case "Array":
flag = typeString === "[object Array]";
break;
case "Date":
flag = typeString === "[object Date]";
break;
case "RegExp":
flag = typeString === "[object RegExp]";
break;
default:
flag = false;
}
return flag;
}; // 处理正则
const getRegExp = re => {
var flags = "";
if (re.global) flags += "g";
if (re.ignoreCase) flags += "i";
if (re.multiline) flags += "m";
return flags;
};
// 维护两个储存循环引用的数组
const parents = [];
const children = []; const _clone = parent => {
if (parent === null) return null;
if (typeof parent !== "object") return parent; let child, proto; if (isType(parent, "Array")) {
// 对数组做特殊处理
child = [];
} else if (isType(parent, "RegExp")) {
// 对正则对象做特殊处理
child = new RegExp(parent.source, getRegExp(parent));
if (parent.lastIndex) child.lastIndex = parent.lastIndex;
} else if (isType(parent, "Date")) {
// 对Date对象做特殊处理
child = new Date(parent.getTime());
} else {
// 处理对象原型
proto = Object.getPrototypeOf(parent);
// 利用Object.create切断原型链
child = Object.create(proto);
} // 处理循环引用
const index = parents.indexOf(parent); if (index != -1) {
// 如果父数组存在本对象,说明之前已经被引用过,直接返回此对象
return children[index];
}
parents.push(parent);
children.push(child); for (let i in parent) {
// 递归
child[i] = _clone(parent[i]);
} return child;
};
return _clone(parent);
};
这个深克隆还不算完美,例如Buffer对象、Promise、Set、Map可能都需要我们做特殊处理,另外对于确保没有循环引用的对象,我们可以省去对循环引用的特殊处理,因为这很消耗时间,不过一个基本的深克隆函数我们已经实现了。
[转] js深拷贝和浅拷贝的更多相关文章
- js 深拷贝和浅拷贝
		
js 深拷贝和浅拷贝 先举一下项目中遇到的两个例子: 例子1: var json = $.parseJSON(data.data);//data.data是接口返回的值var a = json.cha ...
 - 老生常谈之js深拷贝与浅拷贝
		
前言 经常会在一些网站或博客看到"深克隆","浅克隆"这两个名词,其实这个很好理解,今天我们就在这里分析一下js深拷贝和浅拷贝. 浅拷贝 我们先以一个例子来说明 ...
 - 关于JS深拷贝和浅拷贝
		
最近在前端开发中遇到一些问题,就是数组中的某个对象或某个对象的值改变之后,在不刷新页面的时候需要重新渲染值时,页面显示的还是原来的数据.比如: data{ A:[{id:1,num:1},{id:2, ...
 - 在vue中子组件修改props引发的对js深拷贝和浅拷贝的思考
		
不管是react还是vue,父级组件与子组件的通信都是通过props来实现的,在vue中父组件的props遵循的是单向数据流,用官方的话说就是,父级的props的更新会向下流动到子组件中,反之则不行. ...
 - JS 深拷贝和浅拷贝概念,以及实现深拷贝的三种方式
		
一.理解堆栈,基本数据类型与引用数据类型 1.堆栈 栈(stack):系统自动分配的内存空间,内存会由系统自动释放,用来存放函数的参数值,局部变量的值等,特点是先进后出. 堆(heap):系统动态分配 ...
 - 面试遇到的坑JS深拷贝和浅拷贝
		
首先要搞明白深拷贝和钱拷贝的区别要先搞明白 栈和堆的区别 一.栈 栈存储基础数据类型,如: String.Number.Boolean.Null.Underined,这些简单的基础数据类型能够直接存储 ...
 - js深拷贝与浅拷贝
		
1 基础知识:基本类型与引用类型 JS中可以把变量分成两部分,基本类型和引用类型. 基本类型包括:Undefined.Null.Boolean.Number和String: 引用类型值可能由多个值构成 ...
 - js深拷贝、浅拷贝
		
浅拷贝: 只针对当前对象的属性进行拷贝,若当前对象的属性是引用类型时,这个不考虑,不进行拷贝.若属性是引用类型,拷贝后引用的是地址,如果进行更改,会影响拷贝的原对象属性. 深拷贝:针对当前对象的数据的 ...
 - js 深拷贝和浅拷贝理解
		
作者:进击的袋鼠链接:https://www.zhihu.com/question/23031215/answer/124017500来源:知乎著作权归作者所有.商业转载请联系作者获得授权,非商业转载 ...
 
随机推荐
- Hbase关于Java常用API举例
			
1. HBase相关对Admin操作的的API封装在HBaseAdmin中,封装了HBase常用操作的API 使用方法: pom.xml <!-- https://mvnrepository.c ...
 - sql server 维护计划与作业关系区别
			
sql server 维护计划与作业关系区别 对于二者的区别,你可以把维护计划看作是针对数据库进行维护的作业模板.自定义作业具有更广泛的用途,当然,也具有更复杂的操作.所以,如果 仅仅是做个数据库优化 ...
 - get github
			
Git是一个分布式的版本控制系统,最初由Linus Torvalds编写,用作Linux内核代码的管理.在推出后,Git在其它项目中也取得了很大成功,尤其是在Ruby社区中.目前,包括Rubinius ...
 - Xcode模拟器不显示SDK版本,反而显示设备ID的解决办法
			
今天在应用程序中修改了Xcode app 的名称,结果导致Xcode模拟器不显示SDK版本,反而显示设备ID了,感觉特别的忧伤......如图: 进到Xcode->window->Devi ...
 - Observer pattern 观察者模式
			
一.认识观察者模式 我们看看报纸和杂志的订阅是怎么回事: 1.报社的业务就是出版报纸. 2.向某家报社订阅报纸,只要他们有新的报纸出版,就会给你送来,只要你是他们的订户,你就会一直收到报纸. 3.当你 ...
 - Provisioning Services 7.6 入门到精通系列之一:PVS前期规划
			
1. Provisioning Services 产品概述 Provisioning Services (简称PVS)采用了一种与传统映像解决方案截然不同的方法,从根本上改变了硬件与依托硬件而运行的 ...
 - Making the Grade(POJ3666)
			
题目大意: 给出长度为n的整数数列,每次可以将一个数加1或者减1,最少要多少次可以将其变成单调增或者单调减(不严格). 题解: 1.一开始我有一个猜想,就是不管怎么改变,最终的所有数都是原来的某个数. ...
 - 【mysql】关于循环插入数据 存储设计
			
要求插入的数据有一定的规律 新建实例列表 CREATE TABLE users ( userId ) NOT NULL, userName ) NOT NULL, Serves ) NOT NULL, ...
 - SIGKDD历年Best Papers
			
作者:我爱机器学习原文链接:SIGKDD历年Best Papers SIGKDD(Data Mining)(1997-2016) 年份 标题 一作 一作单位 2016 FRAUDAR: Boundin ...
 - asp.net 5 中应用程序根目录及物理文件根目录的获取方式 此文已过期,不再适应rc1以后的版本
			
之前看了asp.net5,小弟就试着用了用,做了个小网站练习一下,有一个小模块需要上传图片到wwwroot下的images文件夹,但是aspnet5 发生了翻天复地变化,之前获取网站根目录的的方法不再 ...