seo查询站长,wordpress建站时间,海南网站建设哪里好,全球速卖通开店需要多少钱js的克隆是一个老生常谈的内容了,今天没啥好写的,就写这个了
要搞清楚js的克隆,就需要先搞清楚js中的数据类型,js中数据类型分为两大类
类型说明原始类型-string字符串类型#xff0c;用于表示文本数据。number数字类型#xff0c;包括整数和浮点数#xff0c;用于表示数值…js的克隆是一个老生常谈的内容了,今天没啥好写的,就写这个了
要搞清楚js的克隆,就需要先搞清楚js中的数据类型,js中数据类型分为两大类
类型说明原始类型-string字符串类型用于表示文本数据。number数字类型包括整数和浮点数用于表示数值数据。boolean布尔类型true 或 false用于表示逻辑值。null空值表示无或不存在任何值。undefined未定义值表示变量未被赋值或初始化。bigint大整数类型可以表示任意大的整数使用n作为后缀如 100n。symbol符号类型用于创建唯一的标识符使用Symbol()创建。引用类型-object对象类型由一组键值对组成用于表示复杂的数据结构使用大括号 {} 创建。
原始类型的拷贝确实相对简单因为这些类型通常存储在栈上这意味着它们在内存中占据的空间是固定的并且可以直接通过指针进行复制。
tips:如果你还搞不清楚什么是引用类型,什么是原始类型,可以看我的这篇文章 javascript数据类型与引用类型的区别以及原始值详解
然而对于引用类型来说拷贝则要复杂得多。引用类型的对象例如数组或对象存储在堆上这意味着它们在内存中占据的空间是不固定的并且由垃圾回收器管理。因此对于引用类型的拷贝我们不能简单地复制指针而是需要复制整个对象。
为了实现引用类型的拷贝我们需要使用深拷贝deep copy或浅拷贝shallow copy的技术。深拷贝会复制对象的所有嵌套对象和数组而浅拷贝则只会复制对象的顶层结构。
需要注意的是深拷贝可能会导致性能问题因为它需要复制大量的数据。因此对于大型的引用类型对象我们可能需要使用一些优化技巧来避免深拷贝例如使用对象代理object proxy或冻结freeze对象来阻止修改。
对于简单对象的拷贝_数组克隆
数组对象作为引用类型,栈内存储的是这个数组对象的堆内引用地址,因为对象类型通常比较庞大这是数据开销和内存开销优化的手段,也就是说,对于数组只通过简单赋值符号赋值的话,是行不通的 示例如下 var x [1,2,3];var y x;console.log(y); //[1,2,3]y.push(4);console.log(y); //[1,2,3,4]console.log(x); //[1,2,3,4]对于这种情况,我们可以通过一个循环,简单粗暴的解决这个问题 var x [1, 2, 3];var y [];x.forEach(v y.push(v))console.log(y); //[1,2,3]y.push(4);console.log(y); //[1,2,3,4]console.log(x); //[1,2,3]对于简单对象的拷贝_简单对象的克隆 var x {a:1,b:2};var y {};for(var i in x){y[i] x[i];}console.log(y); //Object {a: 1, b: 2}y.c 3;console.log(y); //Object {a: 1, b: 2, c: 3}console.log(x); //Object {a: 1, b: 2}当然,我知道还有一种更加简单高效的方法,那就是使用json的序列化.但是这里先不讲它,后面再讲
对于简单对象的拷贝_函数的克隆
由于函数对象克隆之后的对象会单独复制一次并存储实际数据因此并不会影响克隆之前的对象。所以采用简单的复制“”即可完成克隆。 var x function(){console.log(1);};var y x;y function(){console.log(2);};x(); //1y(); //2JavaScript浅克隆和深度克隆
浅克隆Shallow Clone和深度克隆Deep Clone是 JavaScript 中用来复制对象或数组的两种主要方法。它们的主要区别在于复制过程中对嵌套对象或数组的处理方式。
浅克隆 浅克隆只复制对象或数组的顶层元素对于嵌套的对象或数组它只复制了引用而没有复制内部的元素。这意味着如果你改变了复制的对象或数组原对象或数组也会被改变因为它们指向的是同一份数据。深度克隆 深度克隆会复制对象或数组的所有层级的元素。这意味着对于嵌套的对象或数组它会创建完全独立的副本。这样改变复制的对象或数组不会影响到原对象或数组。在 JavaScript 中可以使用 JSON.parse(JSON.stringify(obj)) 方法来进行深度克隆。但是这个方法只能用于对象或数组不包含函数、RegExp、Date、Infinity、NaN、undefined、Infinity、NaN的情况
浅克隆示例如下 // 浅克隆函数function shallowClone(obj) {let clone Array.isArray(obj) ? [] : {}for (let key in obj) {if (obj.hasOwnProperty(key)) {clone[key] obj[key]}}return clone;}// 被克隆对象const oldObj {a: 1,b: [1, 2, 3],c: { d: { e: 2 } }};const newObj shallowClone(oldObj);console.log(newObj.c.d, oldObj.c.d); // { e: 2 } { e: 2 }console.log(oldObj.c.h newObj.c.h); // truenewObj.c.d 100 //改变newObjconsole.log(oldObj.c.d) //oldObj随之改变我们可以很明显地看到,虽然oldObj.c.d被克隆了,但是它还与oldObj.c.d相等,这表明他们依然指向同一段堆内存,我们上面讨论过了引用类型的特点这就造成了如果对newObj.c.d进行修改,也会影响oldObj.c.d。这往往不是我们想要的
所以我们需要构建一个深度克隆函数
深度克隆
上面我们讲过,使用json的序列化和反序列化可以对简单对象进行深度拷贝,而当对象中出现诸如function 、RegExp、Date、Infinity、NaN、undefined 或 Symbol 等等之类的类型时,json的序列化便处理不了,而且有循环引用的时候更是会直接报错
但反过来想,如果我们针对这些特殊情况做处理,那不就能实现深度克隆了吗
所以我们先要获取不同对象的类型做出判断,这样我们就可以对特殊对象进行类型判断了,从而采用针对性的克隆策略. const isType (obj, type) {if (typeof obj ! object) return false// 判断数据类型的经典方法const typeString Object.prototype.toString.call(obj)let flagswitch (type) {case Array:flag typeString [object Array]breakcase Date:flag typeString [object Date]breakcase RegExp:flag typeString [object RegExp]breakdefault:flag false}return flag};测试一下 const arr Array.of(3, 4, 5, 2)console.log(isType(arr, Array))类型识别正常
对于正则对象,我们在处理之前要先补充一点新知识. 我们需要通过正则的扩展了解到flags属性等等,因此我们需要实现一个提取flags的函数 const getRegExp re {var flags if (re.global) flags gif (re.ignoreCase) flags iif (re.multiline) flags mreturn flags}昨晚前置工作,就是把这些方法组合起来了,而且为了防止有循环引用,我们这里使用递归来进行遍历属性 const clone parent {const parents [];const children [];const _clone parent {if (parent null) return null;if (typeof parent ! object) return parent;let child, proto;if (isType(parent, Array)) {child [];} else if (isType(parent, RegExp)) {child new RegExp(parent.source, getRegExp(parent));if (parent.lastIndex) child.lastIndex parent.lastIndex;} else if (isType(parent, Date)) {child new Date(parent.getTime());} else {proto Object.getPrototypeOf(parent);child Object.create(proto);}const index parents.indexOf(parent);if (index ! -1) {return children[index];}parents.push(parent);children.push(child);for (let i in parent) {child[i] _clone(parent[i]);}return child;};return _clone(parent);};声明一个复杂一点的对象来做测试 class person {constructor(pname) {this.name pname}}const Messi new person(Messi);function say() {console.log(hi);}const oldObj {a: say,c: new RegExp(abc, i),d: Messi,};oldObj.b oldObj;const newObj deepClone(oldObj);console.log(newObj)console.log(newObjoldObj)如下,
对于一些对象属性只是原始类型或数组的对象但又有循环嵌套的对象处理方法
如标题所示,其实很多时候,要拷贝的对象没有那么复杂,所以我们可以使用简单一点的方法来实现深拷贝
对于对象属性只是原始类型或数组的对象但又有循环嵌套的对象处理方法如下 方法一 function deepClone(obj) {const objectMap new Map()const _deepClone value {const type typeof valueif (type ! object || type null) return valueif (objectMap.has(value)) return objectMap.get(value)const result Array.isArray(value) ? [] : {}objectMap.set(value, result)for (const key in value) {result[key] _deepClone(value[key])}return result}return _deepClone(obj)}声明一个简单的对象来测试一下 方式二 function deepClone(obj) {return new Promise(res {const { port1, port2 } new MessageChannel()port1.postMessage(obj)port2.onmessage msg {res(msg.data)}})}继续拿刚刚那个对象做测试 let oldObj {a: 11,b: 123,c: [1, 2, 3, 4]}oldObj.d oldObjdeepClone(oldObj).then(v {console.log(v)console.log(newObj oldObj)})输出如下