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 相关文章推荐
转一个日期输入控件,支持FF
Apr 27 Javascript
JavaScript 异步调用框架 (Part 4 - 链式调用)
Aug 04 Javascript
使用js简单实现了tree树菜单
Nov 20 Javascript
JS中判断null、undefined与NaN的方法
Mar 26 Javascript
基于JS代码实现导航条弹出式悬浮菜单
Jun 17 Javascript
jacascript DOM节点——元素节点、属性节点、文本节点
Apr 18 Javascript
Vue.js中的图片引用路径的方式
Jul 28 Javascript
VueRouter导航守卫用法详解
Dec 25 Javascript
vue中动态绑定表单元素的属性方法
Feb 23 Javascript
JS非行间样式获取函数的实例代码
Jun 05 Javascript
Vue.JS实现垂直方向展开、收缩不定高度模块的JS组件
Jun 19 Javascript
纯JS实现五子棋游戏
May 28 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
三个类概括PHP的五种设计模式
2012/09/05 PHP
php中的PHP_EOL换行符详细解析
2013/10/26 PHP
php中文字符串截取方法实例总结
2014/09/30 PHP
ThinkPHP 在阿里云上的nginx.config配置实例详解
2017/10/11 PHP
laravel实现按月或天或小时统计mysql数据的方法
2019/10/09 PHP
javascript读取RSS数据
2007/01/20 Javascript
JavaScript中的闭包原理分析
2010/03/08 Javascript
Draggable Elements 元素拖拽功能实现代码
2011/03/30 Javascript
Jquery api 速查表分享
2015/01/12 Javascript
JS截取字符串实例详解
2015/11/24 Javascript
jquery实现可旋转可拖拽的文字效果代码
2016/01/27 Javascript
mint-ui的search组件在键盘显示搜索按钮的实现方法
2017/10/27 Javascript
bootstrap table sum总数量统计实现方法
2017/10/29 Javascript
javaScript强制保留两位小数的输入数校验和小数保留问题
2018/05/09 Javascript
JS实现烟花爆炸效果
2020/03/10 Javascript
react-router-dom 嵌套路由的实现
2020/05/02 Javascript
Python的迭代器和生成器使用实例
2015/01/14 Python
wxPython中listbox用法实例详解
2015/06/01 Python
使用Python将数组的元素导出到变量中(unpacking)
2016/10/27 Python
关于pip的安装,更新,卸载模块以及使用方法(详解)
2017/05/19 Python
Python 对输入的数字进行排序的方法
2018/06/23 Python
Python格式化日期时间操作示例
2018/06/28 Python
Django框架实现逆向解析url的方法
2018/07/04 Python
Python Learning 列表的更多操作及示例代码
2018/08/22 Python
解决python中 f.write写入中文出错的问题
2018/10/31 Python
利用Python+阿里云实现DDNS动态域名解析的方法
2019/04/01 Python
Python数据可视化实现正态分布(高斯分布)
2019/08/21 Python
Tensorflow中的dropout的使用方法
2020/03/13 Python
python 还原梯度下降算法实现一维线性回归
2020/10/22 Python
Cole Haan官方网站:美国时尚潮流品牌
2017/12/06 全球购物
一篇.NET面试题
2014/09/29 面试题
毕业生幼师求职自荐信
2013/10/01 职场文书
会计求职信
2014/05/29 职场文书
教师自我剖析材料范文
2014/09/30 职场文书
装配车间主任岗位职责
2015/04/08 职场文书
怎么禁用Windows 11快照布局? win11不使用快照布局的技巧
2021/11/21 数码科技