前言

工作中会经常遇到操作数组、对象的情况,你肯定会将原数组、对象进行‘备份’
当真正对其操作时发现备份的也发生改变,此时你一脸懵逼,到时是为啥,不是已经备份了么,怎么备份的数组、对象也会发生变化。
如果你对拷贝原理理解的不透彻,此文或许能提供一点帮助。

javascript数据类型

基本数据类型

string、number、null、undefined、boolean、symbol(ES6新增) 变量值存放在栈内存中,可直接访问和修改变量的值
基本数据类型不存在拷贝,好比如说你无法修改数值1的值

引用类型

Object Function RegExp Math Date 值为对象,存放在堆内存中
在栈内存中变量保存的是一个指针,指向对应在堆内存中的地址。
当访问引用类型的时候,要先从栈中取出该对象的地址指针,然后再从堆内存中取得所需的数据

浅拷贝

为什么备份的数组对象也会发生变化,这里就涉及到你用的‘备份’其实是一种浅拷贝

简单的引用拷贝

var a = [1,2,3,4];
var b = a;
a[0] = 0;
console.log(a,b);

可以看到数组a直接赋值给b,a、b引用的其实是一个对象地址,只要地址值发生变化,a、b栈内存指针指向的堆地址也会发生变化,这种引用拷贝只是新增了一个变量栈内存的指针,意义不大

数组的concat、slice,对象的assign拷贝

同样的例子

var a = [1,2,3,4];
var b = a.concat();
a[0] = 0;
console.log(a,b);

此时数组a[0]值变成0,b数组依然保持不变,有同学就问了,这不就是深拷贝吗。

对,也不对, Array.prototype.slice 和 Array.prototype.concat 看似深拷贝,其实质上还是浅拷贝

var a = [1,2,[3,4],{name:'ccy'}];
var b = a.concat();
a[3].name = 'hs';
console.log(a[3],b[3]);

当数组a中包含对象时, Array.prototype.slice 和 Array.prototype.cancat 拷贝出来数组中的对象还是共享同一内存地址,所以本质上归属浅拷贝

Object.assign 原理也是一样的(对于对象的属性都为基本类型可以当成深拷贝)

var a = {age:18,name:'ccy',info:{address:'wuhan',interest:'playCards'}};
var b = Object.assign(a);
a.info.address = 'shenzhen';
console.log(a.info,b.info);

那怎样才能对对象进行深拷贝呢,请扶好眼镜。

自定义深拷贝函数

var clone = function(obj){
var construct = Object.prototype.toString.call(obj).slice(8,-1);
var res;
if(construct === 'Array'){
res = [];
}else if(construct === 'Object'){
res = {}
}
for(var item in obj){
if(typeof obj[item] === 'object'){
res[item] = clone(obj[item]);
}else{
res[item] = obj[item];
}
}
return res;
};

乍一看好像能处理对象的属性为对象的问题,可以循环遍历直至属性为基本类型;

但是仔细想,如果遇到对象的属性存在相互引用的话会出现死循环的情况。可以再加一次判断,对象的属性如果引用对象指针则跳出当前循环。

深拷贝

深拷贝是可以完美的解决浅拷贝的弊端,重新开辟一块地址,深拷贝出来的属性的基本类型值都是相同的。

JSON内置对象深拷贝

JSON 对象是ES5中引入的新的类型(支持的浏览器为IE8+),JSON对象parse方法可以将JSON字符串反序列化成JS对象,stringify方法可以将JS对象序列化成JSON字符串,借助这两个方法,也可以实现对象的深拷贝

var a = {age:1,name:'ccy',info:{address:'wuhan',interest:'playCards'}};
var b = JSON.parse(JSON.stringify(a));
a.info.address = 'shenzhen';
console.log(a.info,b.info);

JSON 可处理一般的对象进行深拷贝,但是不能处理函数、正则等对象

我们可以对自定义的拷贝函数再进行优化

var clone = function(obj){
function getType(obj){
return Object.prototype.toString.call(obj).slice(8,-1);
}
function getReg(a){
var c = a.lastIndexOf('/');
var reg = a.substring(1,c);
var escMap = {'"': '\\"', '\\': '\\\\', '\b': '\\b', '\f': '\\f', '\n': '\\n', '\r': '\\r', '\t': '\\t', '\w': '\\w', '\s': '\\s', '\d': '\\d'};
for(var i in escMap){
if(reg.indexOf(i)){
reg.replace(i,escMap[i]);
}
}
var attr = a.substring(c+1);
return new RegExp(reg, attr);
}
var construct = getType(obj);
var res;
if(construct === 'Array'){
res = [];
}else if(construct === 'Object'){
res = {}
}
for(var item in obj){
if(obj[item] === obj) continue;//存在引用则跳出当前循环
if(getType(obj[item]) === 'Function'){
res[item] = new Function("return "+obj[item].toString())();
}else if(getType(obj[item]) === 'RegExp'){
res[item] = getReg(obj[item].toString());
}else if(getType(obj[item]) === 'Object'){
res[item] = clone(obj[item]);
}else{
res[item] = obj[item];
}
}
return res;
};

基本可以实现函数、正则对象的深拷贝,在本地只做了简单的测试,如果存在问题,请及时评论指出。

当然像函数库 lodash 的 _.cloneDeep、 JQuery 的 $.extend都实现了深拷贝,有兴趣的同学可自行看下源码。

参考资料

https://developer.mozilla.org/zh-CN/search?q=%E6%B7%B1%E6%8B%B7%E8%B4%9D

https://github.com/wengjq/Blog/issues/3

JavaScript之深拷贝和浅拷贝的更多相关文章

  1. javascript对象深拷贝,浅拷贝 ,支持数组

    javascript对象深拷贝,浅拷贝 ,支持数组 经常看到讨论c#深拷贝,浅拷贝的博客,最近js写的比较多, 所以也来玩玩js的对象拷贝. 下面是维基百科对深浅拷贝的解释: 浅拷贝 One meth ...

  2. 也来玩玩 javascript对象深拷贝,浅拷贝

    经常看到讨论c#深拷贝,浅拷贝的博客,最近js写的比较多, 所以也来玩玩js的对象拷贝. 下面是维基百科对深浅拷贝的解释: 浅拷贝 One method of copying an object is ...

  3. JavaScript的深拷贝和浅拷贝总结

    深拷贝和浅拷贝 深拷贝:拷贝实例:浅拷贝:拷贝引用(原对象). 说深拷贝和浅拷贝之前,我先去了解了下高程书上的JavaScript的变量类型: 基本类型:undefined.null.Boolean. ...

  4. JavaScript的深拷贝和浅拷贝

    一.数据类型 数据分为基本数据类型(String, Number, Boolean, Null, Undefined,Symbol)和对象数据类型.. 1.基本数据类型的特点:直接存储在栈(stack ...

  5. JavaScript的深拷贝与浅拷贝

    深拷贝和浅拷贝是在面试中经常遇到的问题.今天在这里总结一下. 深拷贝与浅拷贝的问题,涉及到JavaScript的变量类型,先来说说变量的类型,变量类型包括基本类型和引用类型. 基本类型:Undefin ...

  6. 详解javascript的深拷贝与浅拷贝

    1. 认识深拷贝和浅拷贝 javascript中一般有按值传递和按引用传递两种复制,按值传递的是基本数据类型(Number,String,Boolean,Null,Undefined),一般存放于内存 ...

  7. JavaScript 的 深拷贝和浅拷贝

    深拷贝和浅拷贝都是针对的引用类型, JS中的变量类型分为值类型(基本类型)和引用类型: 对值类型进行复制操作会对值进行一份拷贝,而对引用类型赋值,则会对地址进行拷贝,最终两个变量指向同一份数据 一.先 ...

  8. javaScript深拷贝和浅拷贝简单梳理

    在了解深拷贝和浅拷贝之前,我们先梳理一下: JavaScript中,分为基本数据类型(原始值)和复杂类型(对象),同时它们各自的数据类型细分下又有好几种数据类型 基本数据类型 数字Number 字符串 ...

  9. 读懂javascript深拷贝与浅拷贝

    1. 认识深拷贝和浅拷贝 javascript中一般有按值传递和按引用传递两种复制,按值传递的是基本数据类型(Number,String,Boolean,Null,Undefined),一般存放于内存 ...

随机推荐

  1. 实现PHP服务端和c#客户端数据交换

    服务端实现功能1,数据库的访问dbhelper.php包括执行语句返回多行,返回json数据,返回单条记录,返回第一行第一列的整数,返回第一行第一列的浮点数,返回第一行第一列的双精度数,返回第一行第一 ...

  2. SQL Server数据库设置自动备份策略

    一. 简单介绍 SQL Server自带的维护计划是一个非常有用的维护工具,能够完成大部分的数据库的维护任务. 数据库的备份也是日常工作中非常重要的一个环节.备份的方法非常的多. 今天给大家介绍最简单 ...

  3. uboot常用的函数

    http://blog.csdn.net/ooonebook/article/details/53206623 以下例子都以project X项目tiny210(s5pv210平台,armv7架构)为 ...

  4. 再谈docker基本命令

    子曰,温故而知新 今日,再次看书之际,又寻得docker的几条使用命令,用小本本记下来 配置docker镜像源 当我们在拉去一些共有镜像时,默认,docker会向docker.io去获取,如果在拉取的 ...

  5. Linux-VMware Workstation&CentOS-5.5-i386-bin-DVD安装

    [2018年6月24日 22:55:47]VM7+CentOS5.5使用NAT方式连接互联网1.在VMWare的菜单:“VM→Setting...” 2.在VMWare的菜单:“Edit→Virtua ...

  6. Excel列A、B、C、D----与列序号的转换

    public static class ExcelConvert { public static int ToExcelIndex(this string columnName) { if (!Reg ...

  7. 可遇不可求的Question之Mysql在不重启服务的情况下修改运行时变量篇

    比方说在一些实际生产环境中,想改个MYSQL的配置,但是又不想停止服务重起MYSQL,有什么办法呢?使用SET命令可以做到,请看下面几个例子: 1.设置key_buffer_size的大小为10M. ...

  8. 利用Python+163邮箱授权码发送邮件

    背景 前段时间写了个自动打卡的脚本,但是脚本不够完善,我需要知道,打卡到底成没成功,因此,我想到了用Python执行完代码之后,再执行一段发送邮件的代码.需求开始明确了,就开始分析和写代码实现吧. 分 ...

  9. 【Solidity】学习(3)

    函数 重定义 不支持重定义,会在编译时候报错 pragma solidity ^0.4.0; contract test { uint public a =100; function changeA ...

  10. wxPython 的简单框架实例

    #coding:utf-8 import wx def load(event): file = open(filename.GetValue(),'r') contents.SetValue(file ...