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 相关文章推荐
javascript基础的动画教程,直观易懂
Jan 10 Javascript
javascript Prototype 对象扩展
May 15 Javascript
JQuery中getJSON的使用方法
Dec 13 Javascript
解析John Resig Simple JavaScript Inheritance代码
Dec 03 Javascript
JS打印gridview实现原理及代码
Feb 05 Javascript
运算符&&的三个不同层次
Apr 07 Javascript
JS实现兼容性好,自动置顶的淘宝悬浮工具栏效果
Sep 18 Javascript
jQuery+AJAX实现遮罩层登录验证界面(附源码)
Sep 13 Javascript
requireJS使用指南
Apr 27 Javascript
详解JavaScript中的自定义事件编写
May 10 Javascript
jQuery实现表单动态添加数据并提交的方法
Jul 19 jQuery
Flutter实现仿微信底部菜单栏功能
Sep 18 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
一个用php3编写的简单计数器
2006/10/09 PHP
php park、unpark、ord 函数使用方法(二进制流接口应用实例)
2010/10/19 PHP
php中ftp_chdir与ftp_cdup函数用法
2014/11/18 PHP
来自chinaz的ajax获取评论代码
2008/05/03 Javascript
javascript 获取select下拉列表值的代码
2009/09/07 Javascript
数组方法解决JS字符串连接性能问题有争议
2011/01/12 Javascript
jquery+css+ul模拟列表菜单具体实现思路
2013/04/15 Javascript
JS代码同步文本框内容的实例方法
2013/07/12 Javascript
JS返回上一页实例代码通过图片和按钮分别实现
2013/08/16 Javascript
JS和JQUERY获取页面大小,滚动条位置,元素位置(示例代码)
2013/12/14 Javascript
JavaScript在for循环中绑定事件解决事件参数不同的情况
2014/01/20 Javascript
setInterval计时器不准的问题解决方法
2014/05/08 Javascript
jquery 为a标签绑定click事件示例代码
2014/06/23 Javascript
jQuery实现自定义下拉列表
2015/01/05 Javascript
jQuery 选择器详解
2015/01/19 Javascript
jQuery ajax 当async为false时解决同步操作失败的问题
2016/11/18 Javascript
javascript实现循环广告条效果
2017/12/12 Javascript
微信小程序WebSocket实现聊天对话功能
2018/07/06 Javascript
JavaScript解析机制与闭包原理实例详解
2019/03/08 Javascript
Node.js在图片模板上生成二维码图片并附带底部文字说明实现详解
2019/08/07 Javascript
VUE项目初建和常见问题总结
2019/09/12 Javascript
vue实现跳转接口push 转场动画示例
2019/11/01 Javascript
VSCode搭建React Native环境
2020/05/07 Javascript
Vue基本指令实例图文讲解
2021/02/25 Vue.js
举例讲解Python中的身份运算符的使用方法
2015/10/13 Python
使用pytorch完成kaggle猫狗图像识别方式
2020/01/10 Python
Python pip install如何修改默认下载路径
2020/04/29 Python
Python获取android设备cpu和内存占用情况
2020/11/15 Python
韩国三大免税店之一:THE GRAND 中文免税店
2016/07/21 全球购物
西班牙多品牌鞋店连锁店:Krack
2018/11/30 全球购物
Groupon西班牙官方网站:在线优惠券和交易,节省高达70%
2021/03/13 全球购物
大学生实习期自我评价范文
2013/10/03 职场文书
机电一体化职业规划书
2014/01/07 职场文书
工伤事故赔偿协议书
2014/10/27 职场文书
业余无线电通联Q语
2022/02/18 无线电
源码分析Redis中 set 和 sorted set 的使用方法
2022/03/22 Redis