学了堆栈内存空间,应该就理解了什么叫简单数据类型存在栈内存,复杂数据类型存在堆内存了。

然后面试中经常会问、业务中也经常会遇到的问题就是深浅拷贝的问题了。

栈内存中简单数据类型直接拷贝就能得到一个副本,但是复杂数据类型的拷贝如果也想得到一个副本,就需要深拷贝了。

浅拷贝:

var  a = 1;

var b = a;

这就是浅拷贝了,虽然你视觉上看上去a = b;但是修改b的值,a不会收影响。因为b是a的一个副本,就像你拷贝了一个文件夹副本一样。修改副本,源文件夹不会受影响。

但这种拷贝情况只局限在简单类型的拷贝:

string、number、boolean、null、undefiend

如果你拷贝一个数组/对象(以数组为例):

var c = [1,2,3];

var d = c;

d.push(4);

console.log(c)

c也被改变了。

但是如果你改变d的整个数据,让他等于一个新的数组(甚至对象)c这时返倒不受影响了。

很奇怪很费解吧?

这是因为:

数组、对象这类复杂类型数据结构,在栈内存里存放的只是指向堆内存中存放数据的地址,

你直接d = c; 拷贝的也是一个副本,但这个副本区别之处是,他并非数据的副本,而是栈内存地址的副本。

这个副本d的地址改变,对c没有影响,而你给d重新指向一个新的数组,就是改变地址了。此时只有d指向了新数组[5,6,7],但是c不受影响,所以打印出来的不变。

但是虽然有两个地址,一个是原、一个是副本。可他们同时指向同一个堆内存的数据。

这样看来,你拷贝出来的d和c用的是同一个数组。

所以d.push执行以后,并不是c也跟着push了,而是c指向的数组和被d.push的是同一个数组。

你可以想象成,同一个房间,d和c都拿着钥匙,这俩钥匙是不同的,但是他们都是同一间屋子的钥匙。d用钥匙打开门往屋子里放了一个东西。那么c打开这个屋子,这个东西也还会在。

以上这些,就是基础知识的解读。

深拷贝

具体深拷贝就是要理解了复杂类型拷贝的缺点,然后再进行弥补。

既然想要复杂类型也像简单类型那样拷贝一个新数据的话,就不单单是拷贝地址了。而是要重新建立一个新数据存放空间,然后挨个把想要拷贝的东西搬进来才行。

所以拷贝前我们得判断一个数据他到底是什么类型的,好“对症下药”:

getType: function (target) {
/*
* @Author: guojufeng@
* @Date: 2017-12-20 15:07:06
* @purpose 获取一个值的类型
* @param {variateName} target: 要获取类型的变量名或对象
* @output {string} result || "null": 返回的参数 - result或者null,为字符串形式的
*/
if (target === null) {
console.log("getType类型判断为: " + target)
return "null";
}
let result = typeof (target);
if (result == "object") {
if (target instanceof Array) {
result = "array";
} else if (target instanceof Object) {
let target = Object.prototype.toString.call(target);
if (target == "[object Object]") {
result = "object";
} else {
result = target;//构造类
}
}
}
console.log("getType类型判断为: " + result)
return result; //返回类型值的字符串形式
}

以上代码,判断一个值的类型,输入值本身,返回字符串形式的类型描述。

如果是简单类型,返回typeof方法返回的对应值即可。

这里特殊处理了null,因为他用typeof返回object。

然后对于复杂类型的数据,再深入判断其实array类型还是object类型。

对object类型中,还有构造类需要区分。直接返回[object String]这样类型的。但其实在深拷贝阶段,直接将其放到object形式处理了。

然后就是深拷贝的代码:

deepClone: function (origin) {
/*
* @Author: guojufeng@
* @Date: 2018-10-30 20:48:44
* @purpose 深度克隆
* @param {variateName} origin: 要克隆的对象变量名
* @output {对应值} 根据origin的类型返回的具体值
*/
let type = this.getType(origin),
target;
if (type == "array") {
target = [];
/* 数组 */
origin.forEach(el => {
// console.log("ele",el)
target.push(this.deepClone(el));
});
} else if (type == "object") {
/* 对象 */
target = {};
for (const key in origin) {
if (origin.hasOwnProperty(key)) {
/* 注意,只拷贝元素身上的,而不拷贝其原型上的值 */
const el = origin[key];
target[key] = this.deepClone(el);
}
}
} else if (type == "function") {
/* 函数 */
target = function () {};
target = origin;
} else {
/* 原始值 */
target = origin;
}
return target;
}

  

对于简单类型,直接进行拷贝

对于函数,新建一个function,然后拷贝

对于数组,新建一个数组,然后 forEach 遍历拷贝。如果循环过程中,数组中嵌套复杂类型,再次递归调用深拷贝方法。

对于对象,新建一个对象,然后for in遍历拷贝非原型值。如果循环过程中,对象中嵌套复杂类型,再次递归调用深拷贝方法。

以上,就是整个逻辑。

声明:

  请尊重博客园原创精神,转载或使用图片请注明:

  博主:xing.org1^

  出处:http://www.cnblogs.com/padding1015/

js库 - 浅拷贝 & 深拷贝的更多相关文章

  1. js变量浅拷贝 深拷贝

    js的变量分为简单数据类型和复杂数据类型(即引用类型). 简单数据类型在内存中占据着固定大小的空间,被保存在栈内存中,在简单数据类型中,当一个变量指向另一个变量时,只是创建了值的副本,两个变量只是占用 ...

  2. JS中有关对象的继承以及实例化、浅拷贝深拷贝的奥秘

    一.属性的归属问题 JS对象中定义的属性和方法如果不是挂在原型链上的方法和属性(直接通过如类似x的方式进行定义)都只是在该对象上,对原型链上的没有影响.对于所有实例共用的方法可直接定义在原型链上这样实 ...

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

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

  4. js对象浅拷贝和深拷贝详解

    js对象浅拷贝和深拷贝详解 作者:i10630226 字体:[增加 减小] 类型:转载 时间:2016-09-05我要评论 这篇文章主要为大家详细介绍了JavaScript对象的浅拷贝和深拷贝代码,具 ...

  5. js中浅拷贝和深拷贝以及深拷贝的实现

    前言:2019年的第一篇分享... 一.什么是基本类型值和引用类型值?ECMAScript包括两个不同类型的值:基本数据类型和引用数据类型.基本数据类型指的是简单的数据段,引用数据类型指的是有多个值构 ...

  6. js 对象浅拷贝和深拷贝

    var model={name:"boy",age:13}; var CopyModel=model; console.log(CopyModel.name); model.nam ...

  7. $.extend()浅拷贝深拷贝

    参考网址:http://bijian1013.iteye.com/blog/2255037 jQuery.extend() 函数用于将一个或多个对象的内容合并到目标对象. 注意:1. 如果只为$.ex ...

  8. 【转载】写一个js库需要怎样的知识储备和技术程度?

    作者:小爝链接:https://www.zhihu.com/question/30274750/answer/118846177来源:知乎著作权归作者所有,转载请联系作者获得授权. 1,如何编写健壮的 ...

  9. js库

    lanchpad用的js库 http://lesscss.org/ https://github.com/EightMedia/hammer.js/wiki/Getting-Started http: ...

随机推荐

  1. mysql安装5.7.20版本

    安 装 命 令:mysqld install 初始化命令:mysqld –initialize 参考这个链接 http://blog.csdn.net/wlc57627451/article/deta ...

  2. java内存配置举例

    常见配置举例  堆大小设置JVM 中最大堆大小有三方面限制:相关操作系统的数据模型(32-bt还是64-bit)限制:系统的可用虚拟内存限制:系统的可用物理内存限制.32位系统下,一般限制在1.5G~ ...

  3. kettle 6.1 通过JS脚本与SwitchCase结合实现目标步骤选择

    场景: 判断抽取的数据在目标库中是否已经存在(同一个病人是否已经存在治疗方案号): 1.若不存在,则GROUPROWNO=1,并Insert into 目标库   ( 判断外关联字段是否为空 ) 2. ...

  4. jquery 多级联动下拉列表含(数据模型)

    方法 /** * 级联 * 联动 * @param url:访问json数据的地址 * @param param:参数 * @param levelIds:页面下拉标签数组,为联动级数 * @priv ...

  5. 转: linux centos7 下安装maven

    转: https://www.tecmint.com/install-apache-maven-on-centos-7/

  6. js object template

    //== Class Definition var Test = function() { var login = $('#m_login'); //== Private Functions var ...

  7. Matlab quad

    1x3−2x−5dx, (from 0 to 1) write a function myfun that computes theintegrand: function y = myfun(x) y ...

  8. maven scope 范围讲解

    解决办法: <dependency>     <groupId>javax.servlet</groupId>     <artifactId>java ...

  9. CentOS 7 安装 Oracle 11.2.0.4

    一.安装环境 CentOS Linux release 7.2.1511 (Core) Oracle Database 11g Release 2 (11.2.0.4) 二.安装前准备 2.1 修改主 ...

  10. Navicat Win 和 Mac 视图类快捷键对比

    Navicat 查询是根据用户需求从数据库提取可读格式的数据,Navicat 提供两个强大的工具与 SQL 查询工作:查询创建工具和查询编辑器,查询创建工具可视觉化地创建查询,查询编辑器可直接编辑查询 ...