日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

EcmaScript对象克隆之谜

發(fā)布時間:2023/12/18 编程问答 30 豆豆
生活随笔 收集整理的這篇文章主要介紹了 EcmaScript对象克隆之谜 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

低配版深拷貝

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 var deepClone = function(currobj){if(typeof currobj !== 'object'){return currobj;}if(currobj instanceof Array){var newobj = [];}else{var newobj = {}}for(var key in currobj){if(typeof currobj[key] !== 'object'){newobj[key] = currobj[key];}else{newobj[key] = deepClone(currobj[key]) }}return newobj }

嘖嘖真是很精巧啊!對于Array和普通Object都做了區(qū)分。但是顯然,借助遞歸實(shí)現(xiàn)的深拷貝如果要克隆層級很多的復(fù)雜對象,容易造成內(nèi)存溢出,咱可以做出一個小小改進(jìn):

看起來酷一點(diǎn)的深拷貝

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 var deepClone = function(currobj){if(typeof currobj !== 'object'){return currobj;}if(currobj instanceof Array){var newobj = [];}else{var newobj = {}}var currQue = [currobj], newQue = [newobj]; //關(guān)鍵在這里while(currQue.length){var obj1 = currQue.shift(),obj2 = newQue.shift();for(var key in obj1){if(typeof obj1[key] !== 'object'){obj2[key] = obj1[key];}else{if(obj1[key] instanceof Array ){obj2[key] = [];}else{obj2[key] = {}};// 妙啊currQue.push(obj1[key]);newQue.push(obj2[key]);}}}return newobj; };

這里利用了兩個隊(duì)列,還算優(yōu)雅的避免了遞歸的弊端。

JSON序列化

還有一種方法是利用JSON的內(nèi)置方法,即所謂的JSON序列化:

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 var deepClone = function(obj){var str, newobj = obj.constructor === Array ? [] : {};if(typeof obj !== 'object'){return;} else if(window.JSON){str = JSON.stringify(obj), //系列化對象newobj = JSON.parse(str); //還原} else {for(var i in obj){newobj[i] = typeof obj[i] === 'object' ? deepClone(obj[i]) : obj[i]; }}return newobj; };

不過不打緊,它與上面方法的效果基本相同。

上面幾種深拷貝的局限

拜托,大家都很懂對象,上面的方法有幾個很大的問題:

  • 遇到對象內(nèi)部的循環(huán)引用直接gg
  • 無法拷貝函數(shù)(typeof 函數(shù) 得到的是 ‘function’),函數(shù)仍是引用類型
  • 無法正確保留實(shí)例對象的原型

于是,我們就要開始改造上面的深拷貝方法來進(jìn)行完美的克隆了!………….么?

等下,你到底要啥

克隆克隆,我們平常把它掛在嘴上,但面對一個對象,我們真正想克隆的是什么?我想在99%的情況下,我們想克隆的是對象的數(shù)據(jù),而保留它的原型引用方法引用,因此上面提到的局限中的第二點(diǎn),基本可以不考慮?,F(xiàn)在咱再來看看怎么解決剩下兩點(diǎn)。

解決循環(huán)引用

首先搞清什么是循環(huán)引用,常見的循環(huán)引用有兩種:

自身循環(huán)引用

1 2 var a = {}; a._self = a;

這種循環(huán)引用可以說很是常見。

多個對象互相引用

1 2 3 4 var a = {}; var b = {}; a.brother = b; b.brother = a;

也不是沒見過,不過這是典型導(dǎo)致對象內(nèi)存無法被回收的寫法,本身就不推薦。

解決之道

目前只找到了針對第一種引用的解決方法,來自于Jquery源碼:

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 jQuery.extend = jQuery.fn.extend = function() {// options是一個緩存變量,用來緩存arguments[i]// name是用來接收將要被擴(kuò)展對象的key// src改變之前target對象上每個key對應(yīng)的value// copy傳入對象上每個key對應(yīng)的valu// copyIsArray判定copy是否為一個數(shù)組// clone深拷貝中用來臨時存對象或數(shù)組的srcvar options, name, src, copy, copyIsArray, clone, target = arguments[0] || {},i = 1,length = arguments.length,deep = false;// 處理深拷貝的情況if (typeof target === "boolean") {deep = target;target = arguments[1] || {};//跳過布爾值和目標(biāo) i++;}// 控制當(dāng)target不是object或者function的情況if (typeof target !== "object" && !jQuery.isFunction(target)) {target = {};}// 當(dāng)參數(shù)列表長度等于i的時候,擴(kuò)展jQuery對象自身if (length === i) {target = this; --i;}for (; i < length; i++) {if ((options = arguments[i]) != null) {// 擴(kuò)展基礎(chǔ)對象for (name in options) {src = target[name]; copy = options[name];// 防止永無止境的循環(huán),這里舉個例子,如var i = {};i.a = i;$.extend(true,{},i);如果沒有這個判斷變成死循環(huán)了if (target === copy) {continue;}if (deep && copy && (jQuery.isPlainObject(copy) || (copyIsArray = jQuery.isArray(copy)))) {if (copyIsArray) {copyIsArray = false;clone = src && jQuery.isArray(src) ? src: []; // 如果src存在且是數(shù)組的話就讓clone副本等于src否則等于空數(shù)組。} else {clone = src && jQuery.isPlainObject(src) ? src: {}; // 如果src存在且是對象的話就讓clone副本等于src否則等于空數(shù)組。}// 遞歸拷貝target[name] = jQuery.extend(deep, clone, copy);} else if (copy !== undefined) {target[name] = copy; // 若原對象存在name屬性,則直接覆蓋掉;若不存在,則創(chuàng)建新的屬性。}}}}// 返回修改的對象return target; };

解決原型的引用

在我們想辦法魔改深拷貝時,先看下以上這么多深拷貝的基本原理:

利用for-in循環(huán)遍歷對象屬性,如果屬性值是對象則深拷貝,不是則直接賦值

于是俺眉頭一皺發(fā)現(xiàn)事情并不簡單,俺上一篇博客已經(jīng)說明:for-in遍歷的是對象以及其原型鏈上可枚舉屬性,因此想在遍歷時對源對象的__proto__做手腳是根本不存在的,__proto__以及它的不可枚舉屬性根本不會被遍歷到??梢酝ㄟ^下面的例子看出:

1 2 3 4 5 6 7 8 9 10 11 12 13 var deepClone = function() {...} // 隨便從上面拿一個 var A = function() {this.val = 1; } A.prototype.log = function() {console.log(this.val); }var obj1 = new A(); var obj2 = deepClone(obj1);console.log(obj1); // A {val: 1} console.log(obj2); // {val: 1, log: function(){...}}

因此,一個解決方法很單純,就是像上面的jQuery.extend方法一樣,自己傳入拷貝的目標(biāo)對象,extend方法本質(zhì)上只是拓展目標(biāo)對象的屬性,使其獲得源對象上的數(shù)據(jù),這樣一來只要我們先創(chuàng)建好符合需求的目標(biāo)對象即可。

另一種方法則是不采用深拷貝,直接取出需要進(jìn)行拷貝的對象的數(shù)據(jù),然后再利用這份數(shù)據(jù)來實(shí)例化和設(shè)置一個新的對象出來

1 2 3 4 5 6 7 8 9 10 11 var Foo = function( obj ){this.name = obj.name;this.sex = obj.sex };Foo.prototype.toJSON = funciton(){return { name: this.name, sex: this.sex }; };var foo = new Foo({ name: "bandit", sex: "male" }); var fooCopy = new Foo( foo.toJSON() );

問題同樣得到解決【鼓掌】


回顧一下,沒有哪種方法是萬用的魔法 —— 在我們想要獲得一個克隆對象之前,或許最好先搞清楚我們到底是在克隆什么,再采用最適合的方法。而非是拘泥于“深拷貝淺拷貝”的說法,去復(fù)制一段代碼祈禱他能生效。我相信以上的示例代碼還沒有考慮到克隆對象的所有問題,但它們在合適的場景下能夠處理合適的問題。嗯,其實(shí)很多事情都是這樣蛤【帶!】

轉(zhuǎn)載于:https://www.cnblogs.com/jinhengyu/p/10257781.html

總結(jié)

以上是生活随笔為你收集整理的EcmaScript对象克隆之谜的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。