集团主站
欢迎来到成都达内官方网站!达内—美国上市公司 亿元级外企IT培训企业!
成都it培训哪家好
成都it培训哪家好
全国服务监督电话:15023458194  |   联系客服   |
当前位置:主页 > 培训课程 > web前端 >

成都前端开发培训:Javascript的对象拷贝

发布者: 成都达内     浏览次数:     发布时间:2019-06-18 10:42:01

本篇将会说说Javascript中对象拷贝的多种方式,以及探究一下深拷贝和浅拷贝。在开始之前,我先提一下一些基础知识:Javascript 的对象只是指向内存中某个位置的指针。...

  本篇将会说说Javascript中对象拷贝的多种方式,以及探究一下深拷贝和浅拷贝。

  在开始之前,我先提一下一些基础知识:Javascript 的对象只是指向内存中某个位置的指针。这些指针是可变的,也就是说,它们可以重新被赋值。因此,单单复制这个指针的结果是,有两个指针指向内存中的同一块地址。

  var foo = {

  a : "abc"

  }

  console.log(foo.a);

  // abc

  var bar = foo;

  console.log(bar.a);

  // abc

  foo.a = "yo foo";

  console.log(foo.a);

  // yo foo

  console.log(bar.a);

  // yo foo

  bar.a = "whatup bar?";

  console.log(foo.a);

  // whatup bar?

  console.log(bar.a);

  // whatup bar?

  如上面的例子可见,foo和bar对象都能根据对方的变化而变化。因此,在拷贝Javascript中的对象时,我们要根据实际使用情况,而做一些考虑。

  成都前端开发培训:Javascript的对象拷贝

  浅拷贝如果要操作的对象拥有的属性都是值类型,那么我们可以用扩展语法或者Object.assign(...)

  var obj = { foo: "foo", bar: "bar" };var copy = { ...obj };// Object { foo: "foo", bar: "bar" }

  var obj = { foo: "foo", bar: "bar" };var copy = Object.assign({}, obj);// Object { foo: "foo", bar: "bar" }

  可以看到,上面的两个方法都可以把多个不同来源对象中的属性拷贝到一个目标对象中。

  var obj1 = { foo: "foo" };var obj2 = { bar: "bar" };var copySpread = { ...obj1, ...obj2 };// Object { foo: "foo", bar: "bar" }var copyAssign = Object.assign({}, obj1, obj2);// Object { foo: "foo", bar: "bar" }

  上面方法的问题是,如果对象的属性本身也是对象,那么实际被拷贝的只是那些指针,也就是说,这跟执行 var bar = foo; 的效果是一样的,跟第一段代码的做法一样。

  var foo = { a: 0 , b: { c: 0 } };var copy = { ...foo };copy.a = 1;copy.b.c = 2;console.dir(foo);// { a: 0, b: { c: 2 } }console.dir(copy);// { a: 1, b: { c: 2 } }

  深拷贝(有限制)想要深拷贝一个对象,一个可用的解决方法是,先把对象序列化为字符串,然后再把它反序列化回来。

  var obj = { a: 0, b: { c: 0 } };var copy = JSON.parse(JSON.stringify(obj));

  不幸的是,这个方法只在对象包含可序列化值,并且没有循环引用的时候有用。其中一个不可序列化的类型的就是日期对象 - 尽管它显示出来是字符串化的ISO格式,JSON.parse只会把它解析成为一个字符串,而不是日期类型。

  深拷贝(少一点限制)对于一些更复杂的情景,我们可以使用HTML5的一个新算法,叫做结构化克隆。不过,截至本篇文章发表为止,有些内置类型还是无法支持,但相比JSON.parse,它支持的类型要多的多:日期类型,正则表达式,Map,集合,二进制大对象,文件集合,图像数据,sparse函数和数组。 它还维护克隆对象的引用,使得他可以支持循环引用结构的拷贝,而这些在上面的序列化例子中是不支持的。

  目前,没有直接调用结构化克隆的方法,但是有些新的浏览器特性,底层使用了这个算法。因此,深拷贝对象可能需要一系列的环境才能实现。

  通过 MessageChannels: 这样做的原理是,借用通讯的一个特性中使用到的序列化算法。由于那个特性是基于事件的,所以这里的克隆也是一个异步操作。

  class StructuredCloner { constructor() { this.pendingClones_ = new Map();this.nextKey_ = 0;const channel = new MessageChannel();this.inPort_ = channel.port1;this.outPort_ = channel.port2;this.outPort_.onmessage = ({data: {key, value}}) => { const resolve = this.pendingClones_.get(key);resolve(value);this.pendingClones_.delete(key);};this.outPort_.start();} cloneAsync(value) { return new Promise(resolve => { const key = this.nextKey_++;this.pendingClones_.set(key, resolve);this.inPort_.postMessage({key, value});});}}const structuredCloneAsync = window.structuredCloneAsync = StructuredCloner.prototype.cloneAsync.bind(new StructuredCloner);const main = async () => { const original = { date: new Date(), number: Math.random() };original.self = original;const clone = await structuredCloneAsync(original);// different objects: console.assert(original !== clone);console.assert(original.date !== clone.date);// cyclical: console.assert(original.self === original);console.assert(clone.self === clone);// equivalent values: console.assert(original.number === clone.number);console.assert(Number(original.date) === Number(clone.date));console.log("Assertions complete.");};main();

  通过history对象API:history.pushState() 和history.replaceState() 都会给它们的第一个参数做一个结构化克隆!要注意的是,这个方法是同步的,操作浏览器历史这个操作速度不是非常快,如果频繁调用这个方法,会导致浏览器卡死。

  const structuredClone = obj => { const oldState = history.state;history.replaceState(obj, null);const clonedObj = history.state;history.replaceState(oldState, null);return clonedObj;};

  通过 notification API: 当创建一个notification实例的时候,构造器为它相关的数据做了结构化克隆。需要注意的是,它会尝试向用户展示浏览器通知,但是,除非它接收到用户允许展示通知的请求,否则,它什么也不会做。一旦用户点击同意的话,notification 会立刻被关闭。

  const structuredClone = obj => { const n = new Notification("", {data: obj, silent: true});n.onshow = n.close.bind(n);return n.data;};

  使用Node.js进行深拷贝Node.js的8.0.0版本提供了一个 序列化 api 可以跟结构化克隆媲美. 不过这个API在本文发布的时候,还只是被认为是试验性的:

  const v8 = require('v8');const buf = v8.serialize({a: 'foo', b: new Date()});const cloned = v8.deserialize(buf);cloned.b.getMonth();

  8.0.0版本以下的话,比较稳定的方法,可以考虑用 lodash的cloneDeep函数,它的思想也多少有点基于结构化克隆算法。

  成都前端开发培训:Javascript的对象拷贝

  结论总结一下,Javascript 中最好的对象拷贝的算法,很大程度上取决于使用环境,以及你需要拷贝的对象的类型。虽然lodash是最安全的泛型深拷贝函数,但是,如果你自己封装的话,可能可以得到效率更高的实现方法,以下就是一个简单的深拷贝,也同样适用于Date日期对象:

  function deepClone(obj) { var copy;// Handle the 3 simple types, and null or undefined if (null == obj || "object" != typeof obj) return obj;// Handle Date if (obj instanceof Date) { copy = new Date();copy.setTime(obj.getTime());return copy;} // Handle Array if (obj instanceof Array) { copy = [];for (var i = 0, len = obj.length;i < len;i++) { copy[i] = deepClone(obj[i]);} return copy;} // Handle Function if (obj instanceof Function) { copy = function() { return obj.apply(this, arguments);} return copy;} // Handle Object if (obj instanceof Object) { copy = {};for (var attr in obj) { if (obj.hasOwnProperty(attr)) copy[attr] = deepClone(obj[attr]);} return copy;} throw new Error("Unable to copy obj as type isn't supported " + obj.constructor.name);}

  就个人来说,我很期待可以随便使用结构克隆的那一天,让对象拷贝不再令人头疼。

(责任编辑:范老师)
最新开班
  • 成都Java培训班
    免费试听名额发放中...
  • 成都C++培训班
    免费试听名额发放中...
  • 成都PHP培训班
    免费试听名额发放中...
  • 成都网络工程培训班
    免费试听名额发放中...
  • 成都Unity3D培训班
    免费试听名额发放中...
  • 成都大数据培训班
    免费试听名额发放中...
  • 成都uid培训班
    免费试听名额发放中...
  • 成都会计培训班
    免费试听名额发放中...
  • 成都Python培训班
    免费试听名额发放中...
  • 成都嵌入式培训班
    免费试听名额发放中...
  • 成都web培训班
    免费试听名额发放中...
  • 成都软件测试培训班
    免费试听名额发放中...
在线留言
提交

校区地址:绵阳市涪城区临园路东段68号富临大都会7栋3单元9层12号

联系电话:15023458194

公交路线:富乐路口凯德广场(10路;29路;3路;15路;11路;15a路;71路)

校区地址:成都市锦江区东大街紫东楼端35号明宇金融广场19楼1906室

联系电话:15023458194

公交路线:芷泉街(18路;21路;43路;48路;104路;152路;335路 ) 地铁路线:东门大桥站(地铁2号线)

校区地址:成都市高新区奥克斯广场蜀锦路209号一楼商铺

联系电话:15023458194

公交路线:益州大道锦城大道口(18路;21路;43路;48路;104路;152路;335路 ) 地铁路线:孵化园(地铁1号线)

校区地址:成都锦江区东大街芷泉街229号东方广场C座3楼303

联系电话:15023458194

公交路线:芷泉街(188路;115路;515路;236路;505路;501路;84路 ) 地铁路线:东门大桥站(地铁2号线)

校区地址:成都市武侯区佳灵路3号红牌楼广场2号写字楼11楼1115号

联系电话:15023458194

公交路线:红牌楼东(11路;92路;100路;111路;139路;g28路;快速公交K1/K2) 地铁路线:红牌楼站(地铁3号线)

校区地址:成都市锦江区红星路二段70号四川日报大厦502-2

联系电话:15023458194

公交路线:市二医院站(6路;49路;102路;5路;37路;g92路;) 地铁路线:地铁市二医院(地铁3号线)

校区地址:成都市锦江区东大街芷泉段229号东方广场C座16层

联系电话:15023458194

公交路线:芷泉街(18路;21路;43路;48路;104路;152路;335路 ) 地铁路线:东门大桥站(地铁2号线)

校区地址:四川省成都市武侯区高新科技孵化园9号园区E座7楼

联系电话:15023458194

公交路线:益州大道锦城大道口(18路;21路;43路;48路;104路;152路;335路 ) 地铁路线:孵化园(地铁1号线)

校区地址:成都市人民南路一段86号“城市之心”大厦26楼

联系电话:15023458194

公交路线:成都市人民南路(6路;14路;42路;72路;76路;1010路;)

校区地址:成都市高新区奥克斯广场B座1708

联系电话:15023458194

公交路线:益州大道锦城大道口(18路;21路;43路;48路;104路;152路;335路 ) 地铁路线:孵化园(地铁1号线)

了解达内动态
关注成都达内教育公众号

首页 | 关于达内 | 课程中心 | 专家师资 | 视频教程 | 学员空间 | 校企合作 | 新闻资讯 | 就业指导 | 网站地图

2016-2025 达内时代科技集团有限公司 版权所有 京ICP证8000853号-56