JavaScript浅层克隆与深度克隆示例详解


Posted in Javascript onSeptember 01, 2020

1 相关知识点

  1. 浅克隆就是将栈内存中的引用复制一份,赋给一个新的变量,本质上两个指向堆内存中的同一地址,内容也相同,其中一个变化另一个内容也会变化。
  2. 深克隆就是创建一个新的空对象,开辟一块内存,然后将原对象中的数据全部复制过去,完全切断两个对象间的联系。
  3. 区别:浅克隆和深克隆最大的区别就是对引用值的处理了,即浅克隆之后你改我也改,深克隆之后你改我不改。(PS:原始值的处理一样)
  4. 原始值(栈数据stack):Number,Boolean(false/true),String,undefined,null
  5. 引用值(堆数据heap):Array,Object,function ··· Date,RegExp

2 浅层克隆

在浅层克隆中,原始值的克隆没问题,只是值的拷贝,不会出现你改我改的问题。但是引用值的克隆,就会出现你改我也改的问题,因为浅层克隆的是地址,即指向的是同一空间。

2.1 浅克隆函数

function clone(origin, target) {
  var target = target || {}; 
  //容错,即防止用户不传递目标参数。若用户传递了参数就用,若没传则拿一个空对象当目标
  for (var prop in origin) {
    target[prop] = origin[prop];
  }
  return target;
}

2.2 运用实例

function clone(origin, target) {
  var target = target || {}; 
  for (var prop in origin) {
    target[prop] = origin[prop];
  }
  return target;
}

var obj = {
  name: 'abc',
  age: '18',
  sex: 'male',
  card: ['a', 'b', 'c'],
  book: {
    name: 'ccc',
    sbook: {
      name: 'aaa'
    }
  }
};
var newobj = {};

clone(obj, newobj);

运行代码如下:

JavaScript浅层克隆与深度克隆示例详解

3 深度克隆

进行深度克隆之后,对于引用值的克隆的问题就会和原始值一样我改你不改,因为在深度克隆中虽然是相同的东西,但是指向不同的空间。即深度克隆之后,值各自独立,互不影响。

3.1 深克隆步骤分析

需要进行深度克隆的对象如下:

var obj = {
  name: 'abc', // 原始值
  age: '18', // 原始值
  sex: 'male',// 原始值
  card: ['a', 'b', 'c'], // 引用值
  book: { // 引用值
    name: 'ccc', // 原始值
    sbook: { // 引用值
      name: 'aaa'// 原始值
    }
  }
};

var obj1 = {};

(1)首先需要遍历要克隆的对象

方法:for (var prop in origin){···}

for (var prop in origin) {
	···
}

(2)依次判断是不是原始值

方法:typeof() ,即若为原始值,就直接拷贝;若为引用值(typeof(···)返回的值是object),则进行递归操作。需要注意是的typeof(null) == 'object',所以得排除这一个情况。

if (origin[prop] !== "null" && typeof(origin[prop]) == 'object') {
	···
	// 递归
} else {
  target[prop] = origin[prop];
}

(3)判断是数组还是对象

方法:toString(推荐), constructor,instanceof (后两个会涉及到父子域的小问题,虽然遇到的可能不是很大)

var toStr = Object.prototype.toString,
  arrStr = "[object Array]";
if (toStr.call(origin[prop]) == arrStr) {
	··· // 数组
} else {
	··· // 对象
}

(4)建立相应的数组或对象

方法:建立一个新的同名空数组 / 对象,并将原始对象中的 数组或对象 当成一个新的原始对象,再次将其中的数据拷贝到目标对象的 同名空数组 / 对象 里面。即递归开始拷贝数组或对象里面的数据,并递归执行第(1)步。递归完成之后,再依次进行下一个数据的克隆。

var toStr = Object.prototype.toString,
  arrStr = "[object Array]";
if (toStr.call(origin[prop]) == arrStr) {
	target[prop] = [];
} else {
	target[prop] = {};
}
newobj = {
  name: 'abc',
  age: '18',
  sex: 'male',
  card: [] 
  // 建立一个新的同名空数组,并把obj的card数据当成一个原始对象,再次拷贝到obj1的card里面
  // 即 递归开始拷贝数组或对象里面的数据,递归执行第(1)步
  // 执行完数组card拷贝之后,开始同理拷贝下一个对象book···
}

3.2 深克隆函数

function deepClone(origin, target) {
  var target = target || {},
    toStr = Object.prototype.toString,
    arrStr = "[object Array]";
  for (var prop in origin) {
    if (origin.hasOwnProperty(prop)) {
      if (origin[prop] !== "null" && typeof(origin[prop]) == 'object') {
        if (toStr.call(origin[prop]) == arrStr) {
          target[prop] = [];
        } else {
          target[prop] = {};
        }
        deepClone(origin[prop], target[prop]);

      } else {
        target[prop] = origin[prop];
      }
    }
  }
  return target;
}

使用三目运算符简化后的代码如下:

// 使用三目运算符简化后
function deepClone(origin, target) {
  var target = (target || {}),
    toStr = Object.prototype.toString,
    arrStr = "[object Array]";
  for (var prop in origin) {
    if (origin.hasOwnProperty(prop)) {
      if (origin[prop] !== "null" && typeof (origin[prop]) == 'object') {
        target[prop] = toStr.call(origin[prop]) == arrStr ? [] : {};
        deepClone(origin[prop], target[prop]);
      } else {
        target[prop] = origin[prop];
      }
    }
  }
  return target;
}

3.3 运用实例

// 使用三目运算符简化后
function deepClone(origin, target) {
  var target = (target || {}),
    toStr = Object.prototype.toString,
    arrStr = "[object Array]";
  for (var prop in origin) {
    if (origin.hasOwnProperty(prop)) {
      if (origin[prop] !== "null" && typeof (origin[prop]) == 'object') {
        target[prop] = toStr.call(origin[prop]) == arrStr ? [] : {};
        deepClone(origin[prop], target[prop]);
      } else {
        target[prop] = origin[prop];
      }
    }
  }
  return target;
}

运行代码如下:

JavaScript浅层克隆与深度克隆示例详解

3.4 hasOwnProperty

hasOwnProperty() 方法会返回一个布尔值,指示对象自身属性中是否具有指定的属性(即是否有指定的键)。

语法:obj.hasOwnProperty(prop)
参数:要检测的属性的字符串形式表示的名称,或者Symbol。
返回值:用来判断某个对象是否含有指定的属性的布尔值。
描述:所有继承了Object的对象都会继承到hasOwnProperty方法。这个方法可以用来检测一个对象是否含有特定的自身属性;和in运算符不同,该方法会忽略掉那些从原型链上继承到的属性。
用法:
a. 使用hasOwnProperty方法判断属性是否存在
b. 区别自身属性与继承属性
c. 遍历一个对象的所有自身属性
d. 使用hasOwnProperty作为属性名
具体知识点请参考 Object.prototype.hasOwnProperty()

若对象里面编写了原型属性,但遍历的时候并不想让其显示出来,就可以使用对象名.hasOwnProperty(属性名) 来判断是否是自身属性,若是自己的则返回值为true,若不是自身原型属性则返回值为false。实例如下:

var obj = {
	name: 'ABC',
	age: '18',
	sex: 'male',
	__proto__: {
		heart: 'happy'
	}
}
for (var prop in obj) {
	// 配套使用,起到一个过滤的作用,不把原型链上的数据弄出来
	if (obj.hasOwnProperty(prop)) {
		console.log(obj[prop]);// ABC 18 male
	}
}

个人笔记,欢迎大家交流探讨!

总结

到此这篇关于JavaScript浅层克隆与深度克隆的文章就介绍到这了,更多相关JavaScript浅层克隆与深度克隆内容请搜索三水点靠木以前的文章或继续浏览下面的相关文章希望大家以后多多支持三水点靠木!

Javascript 相关文章推荐
jQuery实现购物车多物品数量的加减+总价计算
Jun 06 Javascript
angular.foreach 循环方法使用指南
Jan 06 Javascript
基于JavaScript实现移动端点击图片查看大图点击大图隐藏
Nov 04 Javascript
详解JavaScript中的事件流和事件处理程序
May 20 Javascript
jquery轮播的实现方式 附完整实例
Jul 28 Javascript
AngularJS实现Input格式化的方法
Nov 07 Javascript
JS 组件系列之Bootstrap Table 冻结列功能IE浏览器兼容性问题解决方案
Jun 30 Javascript
细说webpack源码之compile流程-rules参数处理技巧(2)
Dec 26 Javascript
全新打包工具parcel零配置vue开发脚手架
Jan 11 Javascript
JS实现DOM节点插入操作之子节点与兄弟节点插入操作示例
Jul 30 Javascript
vue项目中在外部js文件中直接调用vue实例的方法比如说this
Apr 28 Javascript
Vue + Scss 动态切换主题颜色实现换肤的示例代码
Apr 27 Javascript
VUE子组件向父组件传值详解(含传多值及添加额外参数场景)
Sep 01 #Javascript
vue离开当前页面触发的函数代码
Sep 01 #Javascript
Vue 实现监听窗口关闭事件,并在窗口关闭前发送请求
Sep 01 #Javascript
Node.js web 应用如何封装到Docker容器中
Sep 01 #Javascript
Vue中关闭弹窗组件时销毁并隐藏操作
Sep 01 #Javascript
Vue 使用typescript如何优雅的调用swagger API
Sep 01 #Javascript
vue路由切换时取消之前的所有请求操作
Sep 01 #Javascript
You might like
古巴咖啡 Cubita琥爵咖啡 独特的加勒比海风味咖啡
2021/03/06 新手入门
967 个函式
2006/10/09 PHP
php Ajax乱码
2008/04/09 PHP
PHP创建XML接口示例
2019/07/04 PHP
Laravel框架验证码类用法实例分析
2019/09/11 PHP
DOM精简教程
2006/10/03 Javascript
JavaScript replace(rgExp,fn)正则替换的用法
2010/03/04 Javascript
从jquery的过滤器.filter()方法想到的
2013/09/29 Javascript
Node.js中创建和管理外部进程详解
2014/08/16 Javascript
node.js中的fs.mkdir方法使用说明
2014/12/17 Javascript
javascript中window.open在原来的窗口中打开新的窗口(不同名)
2015/11/15 Javascript
Vue.js每天必学之过滤器与自定义过滤器
2016/09/07 Javascript
javascript 定时器工作原理分析
2016/12/03 Javascript
jQuery实现删除li节点的方法
2016/12/06 Javascript
vue2.X组件学习心得(新手必看篇)
2017/07/05 Javascript
利用JavaScript如何查询某个值是否数组内
2017/07/30 Javascript
javascript 作用于作用域链的详解
2017/09/27 Javascript
apicloud拉起小程序并传递参数的方法示例
2018/11/21 Javascript
微信小程序云开发使用方法新手初体验
2019/05/16 Javascript
Vue3 中的数据侦测的实现
2019/10/09 Javascript
逐行分析鸿蒙系统的 JavaScript 框架(推荐)
2020/09/17 Javascript
[04:11]2014DOTA2国际邀请赛 CIS遗憾出局梦想不灭
2014/07/09 DOTA
用python生成1000个txt文件的方法
2018/10/25 Python
python SQLAlchemy 中的Engine详解
2019/07/04 Python
Python命令行参数解析工具 docopt 安装和应用过程详解
2019/09/26 Python
解决python中的幂函数、指数函数问题
2019/11/25 Python
python NumPy ndarray二维数组 按照行列求平均实例
2019/11/26 Python
Python3 把一个列表按指定数目分成多个列表的方式
2019/12/25 Python
完美解决jupyter由于无法import新包的问题
2020/05/26 Python
Origins悦木之源香港官网:雅诗兰黛集团高端植物护肤品牌
2018/03/21 全球购物
阿迪达斯印尼官方网站:adidas印尼
2020/02/10 全球购物
Java面试题:Java类的Main方法如果是Private将会怎么样
2016/08/18 面试题
平面设计专业求职信
2014/08/09 职场文书
幼儿园大班教育随笔
2015/08/14 职场文书
AngularJS实现多级下拉框
2022/03/25 Javascript
Redis主从复制操作和配置详情
2022/09/23 Redis