Javascriptdə obyektin dərin klonlanması üçün ən səmərəli yol nədir?

JavaScript obyektini klonlamaq üçün ən səmərəli yol nədir? Mən obj = eval(uneval(o)); lakin qeyri-standart və yalnız Firefox tərəfindən dəstəklənir .

obj = JSON.parse(JSON.stringify(o)); kimi şeylər etdik obj = JSON.parse(JSON.stringify(o)); , lakin effektivliyinə şübhə ilə yanaşır.

Mən də təkrarlanan surətlərini müxtəlif qüsurlarla görmüşəm.

Mən canonical həll yoxdur ki, təəccüb.

4663
23 сент. jschrab tərəfindən müəyyən 23 sep . 2008-09-23 19:26 '08 at 7:26 pm 2008-09-23 19:26
@ 66 cavab
  • 1
  • 2
  • 3

Qeyd: Bu sualın cavabı deyil, başqa cevabın cavabıdır. Əgər obyektləri tez bir şəkildə klon etmək istəyirsinizsə, bu sualın cavabında Corbanın məsləhətinə əməl edin.


Qeyd etmək istəyirəm ki, jQuery-.clone() metodu yalnız DOM elementlərini .clone() . JavaScript obyektlərini klonlamaq üçün:

 // Shallow copy var newObject = jQuery.extend({}, oldObject); // Deep copy var newObject = jQuery.extend(true, {}, oldObject); 

Daha ətraflı məlumatı jQuery sənədlərində tapa bilərsiniz.

Mən də dərin bir surətin yuxarıda göstərildiyi kimi daha ağıllı olduğunu qeyd etmək istəyirəm - bu, bir çox tələlərin (məsələn, DOM elementini dərinləşdirmək üçün) qarşısını ala bilər. Tez-tez jQuery core və böyük təsir ilə plugins istifadə olunur.

4122
23 сент. John Resig tərəfindən verilmiş cavab 23 sentyabr 2008-09-23 21:09 '08 at 09:09 pm 2008-09-23 21:09

Bu göstəriciyə baxın: http://jsben.ch/#/bWfk9

Əvvəlki testlərimdə, sürətin əsas problemi olduğu yerdən kəşf etdim

<Əvvəlki <code> JSON.parse (JSON.stringify (OBJ)) Kodu>

bir obyektin dərin klonlanması üçün ən sürətli yol (bu jQuery.extenddən 10-20% -ə qədər dərin bayraqla üstünlənir ).

Dərin dəyər bayrağının saxta (sığ klon) olduğu zaman jQuery.extend olduqca sürətli. Bu, yaxşı bir seçimdir, çünki tip yoxlanışı üçün əlavə məntiq daxildir və təsvirin təsviri və s. Üzərində kopyalanmır. Ancaq bir az yavaşlayacaq.

Əgər klon etməyə çalışdığınız obyektlərin strukturunu bilirsinizsə və ya dərin iç içə dizilərdən qaçınarsan, obyektin klonlanması for (var я in obj) loop for (var я in obj) sadə for (var я in obj) objdə var) yaza bilərsiniz, hasOwnProperty yoxlanılır və jQuery-dən çox daha sürətli olacaq.

border=0

Nəhayət, bir isti loopda bilinən bir obyekt strukturunu klonlamağa çalışırsınızsa, klonlama prosedurunu sadəcə olaraq yerləşdirməklə və obyektin əllə yaradılması ilə daha çox PERFORMANS əldə edə bilərsiniz.

JavaScript tracking mexanizmləri üçün optimallaşdırmaq üçün sormaq for..in loops və hasOwnProperty yoxlanılması da yavaşlatmaq olacaq. Sürət olduqda manuel klon mütləq bir zərurətdir.

  var clonedObject = { knownProp: obj.knownProp,.. } Код> 

Date obyektləri üçün JSON.parse(JSON.stringify(obj)) metodunu JSON.parse(JSON.stringify(obj)) istifadə edərək çəkinin - JSON.stringify(новая дата()) JSON.parse() Date obyektinə qayıtmayan ISO formatında tarixin simli JSON.parse() qaytarır. Daha ətraflı məlumat üçün bu cavabı baxın .

Buna əlavə olaraq, Chrome 65-də, ən azı, doğma klonlama uyğun deyil. Bu JSPerf görə, yeni bir funksiya yaratmaqla öz klonlama etdiyiniz JSON.stringify istifadə edərək, təxminən 800x yavaş, bütün board vasitəsilə olduqca sürətli keçir.

1951
17 марта '11 в 22:19 2011-03-17 22:19 cavab 17 Mart 2011-ci il saat 22:19 da Corban Brook tərəfindən verilmişdir 2011-03-17 22:19

Yalnız sizin dəyişənlərə malik olduğunuzu və obyektinizdə heç bir funksiya olmadığını düşünsəniz, sadəcə istifadə edə bilərsiniz:

 var newObject = JSON.parse(JSON.stringify(oldObject)); 
412
04 янв. Cavab Şan Şakir tərəfindən 04 yanvar 2011-01-04 11:05 '11 at 11:05 2011-01-04 11:05

Əgər inşa edilməmişsə, şübhə edə bilərsiniz:

 function clone(obj) { if (obj === null || typeof (obj) !== 'object' || 'isActiveClone' in obj) return obj; if (obj instanceof Date) var temp = new obj.constructor(); //or new Date(obj); else var temp = obj.constructor(); for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) { obj['isActiveClone'] = null; temp[key] = clone(obj[key]); delete obj['isActiveClone']; } } return temp; } 
285
23 сент. ConroyP tərəfindən verilmiş cavab 23 sentyabr 2008-09-23 19:38 '08 at 7:38 pm 2008-09-23 19:38

Strukturlaşdırılmış klonlama

HTML5, obyektlərin dərin klonları yarada bilən daxili "strukturlaşdırılmış" klonlama alqoritmini təyin edir. JSON tərəfindən dəstəklənən bir neçə növə əlavə olaraq, hələ də müəyyən daxili quraşdırılmış növlərlə məhdudlaşır, həmçinin, Tarixlər, RegExps, Xəritələr, Sets, Blobs, FileLists, ImageDatas, nadir seriallar, Typed Arrays və daha çox gələcəkdə də dəstəkləyir. JSON üçün səhvlərə səbəb ola biləcək çevik və təkrarlanan strukturların saxlanmasına imkan verən klonlanan məlumatlarda istinadlar saxlayır.

Brauzerlərdə birbaşa dəstək: Tezliklə? 🙂

Brauzerlər hazırda konfiqurasiya edilmiş klonlama alqoritmasına birbaşa interfeys təmin etmir, lakin qlobal structuredClone() funksiyası GitHub üzrə hansı html # 793-də fəal şəkildə müzakirə olunur və tezliklə görünə bilər! Hal-hazırda təklif olunduğu kimi, bir çox məqsədi üçün istifadə aşağıdakı kimi sadə olacaq:

 const clone = structuredClone(original); 

Göndərilməyə qədər, strukturlaşdırılmış brauzer klonlarının tətbiqləri yalnız dolaylı olaraq göstərilir.

Asynchronous həlli: istifadə edilə bilər. 😕

Mövcud API ilə strukturlaşdırılmış bir klon yaratmaq üçün aşağı yol bir MessageChannels portu vasitəsilə məlumatı göndərməkdir. Digər port əlavə edilmiş bir .data strukturlaşdırılmış klonuyla bir message hadisə yaradır. Təəssüf ki, bu hadisələri dinləmək mütləq asinxronlaşdırıla bilər və sinxron alternativlər daha az praktikdir.

 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); 

İstifadə nümunəsi:

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

İstifadə nümunəsi:

Kodun bir xəttində bir obyektin klonlanması (dərin klonlama deyil) üçün effektiv bir yol

Object.assign metodu ECMAScript 2015 (ES6) standartının bir hissəsidir və sizə lazım olanı tam olaraq edir.

 var clone = Object.assign({}, obj); 

Object.assign () metodu, bir və ya daha çox mənbə obyektindən hədəf obyektinə qədər sayılan bütün müvafiq xüsusiyyətlərin dəyərlərini kopyalamaq üçün istifadə olunur.

Daha ətraflı ...

köhnə brauzerləri dəstəkləmək üçün polifillə :

 if (!Object.assign) { Object.defineProperty(Object, 'assign', { enumerable: false, configurable: true, writable: true, value: function(target) { 'use strict'; if (target === undefined || target === null) { throw new TypeError('Cannot convert first argument to object'); } var to = Object(target); for (var i = 1; i < arguments.length; i++) { var nextSource = arguments[i]; if (nextSource === undefined || nextSource === null) { continue; } nextSource = Object(nextSource); var keysArray = Object.keys(nextSource); for (var nextIndex = 0, len = keysArray.length; nextIndex < len; nextIndex++) { var nextKey = keysArray[nextIndex]; var desc = Object.getOwnPropertyDescriptor(nextSource, nextKey); if (desc !== undefined  desc.enumerable) { to[nextKey] = nextSource[nextKey]; } } } return to; } }); } 
140
15 дек. Cavab Eugene Tiurin tərəfindən verilir 15 Dek 2015-12-15 10:26 '15 at 10:26 AM 2015-12-15 10:26

kod:

 // extends 'from' object with members from 'to'. If 'to' is null, a deep clone of 'from' is returned function extend(from, to) { if (from == null || typeof from != "object") return from; if (from.constructor != Object  from.constructor != Array) return from; if (from.constructor == Date || from.constructor == RegExp || from.constructor == Function || from.constructor == String || from.constructor == Number || from.constructor == Boolean) return new from.constructor(from); to = to || new from.constructor(); for (var name in from) { to[name] = typeof to[name] == "undefined" ? extend(from[name], null) : to[name]; } return to; } 

Test:

 var obj = { date: new Date(), func: function(q) { return 1 + q; }, num: 123, text: "asdasd", array: [1, "asd"], regex: new RegExp(/aaa/i), subobj: { num: 234, text: "asdsaD" } } var clone = extend(obj); 
91
25 июня '09 в 10:53 2009-06-25 10:53 Cavab Kamarey tərəfindən 25 iyun 'da 10:53' də verildi. 2009-06-25 10:53

Mən bu istifadə edirəm:

 function cloneObject(obj) { var clone = {}; for(var i in obj) { if(typeof(obj[i])=="object"  obj[i] != null) clone[i] = cloneObject(obj[i]); else clone[i] = obj[i]; } return clone; } 
81
12 дек. Cavab 12 dekabrda Alan tərəfindən verilir . 2009-12-12 01:47 '09 da 1:47 'da 2009-12-12 01:47

Dərin kopiya performansı: yaxşıdan ən pisə

  • Yeniden təyin etmə "=" (string array, sayısal array - yalnız)
  • Dilim (dize simvolları, ədədlərin diziləri - yalnız)
  • Birləşmə (yalnız simli dizilər, sayısal seriallar)
  • Xüsusi funksiya: for-loop və ya recursive surəti
  • jQuery $ .sərhələ
  • JSON.parse (yalnız simli dizilər, ədədlər arrayları, obyektlərin diziləri)
  • Underscore.js _.clone (yalnız simli array, sayısal array)
  • Lo-Dash _.cloneDeep

Dərin bir sıra dizgələr və ya nömrələri kopyalayın (bir səviyyə - heç bir göstərici yoxdur):

Bir sıra numara və strinqlər olduqda - s.slice (),. Concat (),. Splice (), təyin operatoru "=" və Underscore.js klon funksiyası; array elementlərinin dərin surətini çıxarın.

Yenidən təyinat ən yüksək performansı varsa:

 var arr1 = ['a', 'b', 'c']; var arr2 = arr1; arr1 = ['a', 'b', 'c']; 

I.slice () daha yaxşı performansı var .concat (), http://jsperf.com/duplicate-array-slice-vs-concat/3

 var arr1 = ['a', 'b', 'c']; // Becomes arr1 = ['a', 'b', 'c'] var arr2a = arr1.slice(0); // Becomes arr2a = ['a', 'b', 'c'] - deep copy var arr2b = arr1.concat(); // Becomes arr2b = ['a', 'b', 'c'] - deep copy 

Dərindən obyektlərin bir sıra kopyalayın (iki və ya daha çox səviyyə - göstəricilər):

 var arr1 = [{object:'a'}, {object:'b'}]; 

Xüsusi funksiyanı yazın ($ .extend () və ya JSON.parse'dən daha yaxşı işləmə var):

 function copy(o) { var out, v, key; out = Array.isArray(o) ? [] : {}; for (key in o) { v = o[key]; out[key] = (typeof v === "object"  v !== null) ? copy(v) : v; } return out; } copy(arr1); 

Üçüncü tərəfin kommunal funksiyalarını istifadə edin:

 $.extend(true, [], arr1); // Jquery Extend JSON.parse(arr1); _.cloneDeep(arr1); // Lo-dash 

Burada jQuery $ .sertifikatın daha yaxşı performansı var:

67
18 сент. Cavab tfmontague 18 sep verilir . 2014-09-18 23:10 '14 at 23:10 2014-09-18 23:10
 var clone = function() { var newObj = (this instanceof Array) ? [] : {}; for (var i in this) { if (this[i]  typeof this[i] == "object") { newObj[i] = this[i].clone(); } else { newObj[i] = this[i]; } } return newObj; }; Object.defineProperty( Object.prototype, "clone", {value: clone, enumerable: false}); 
57
26 дек. cavab 26 dekabrda Zibri tərəfindən verilir . 2009-12-26 17:59 '09 at 17:59 'da 2009-12-26 17:59

Bilirəm ki, bu, köhnə bir vəzifədir, amma büdrəyəyən birinə kömək edə biləcəyini düşünürəm.

Bir şeyə bir şey atamadığınız müddətcə yaddaşda bir istinad saxlamır. Beləliklə, digər obyektlər arasında bölüşmək istədiyiniz bir obyekt yaratmaq üçün belə bir zavod yaratmalısınız:

 var a = function(){ return { father:'zacharias' }; }, b = a(), c = a(); c.father = 'johndoe'; alert(b.father); 
50
24 сент. Joe tərəfindən verilmiş cavab 24 Sep. 2011-09-24 22:28 '11 'da 22:28' də 2011-09-24 22:28

Bir kütləvi kitabxana ("klon" adlanır) , bu olduqca yaxşıdır. Mən bildiyim təsadüfi obyektlərin ən tam recursiv klonlanması / kopyalanmasını təmin edir. Həmçinin digər cavablarla əhatə olunmayan dairəvi əlaqələri də dəstəkləyir.

Siz npm haqqında tapa bilərsiniz. Həm brauzer, həm də Node.js. üçün istifadə edilə bilər.

Burada istifadə etmək üçün bir nümunədir:

Quraşdırın

 npm install clone 

və ya Ender ilə yığın .

 ender build clone [...] 

Siz həmçinin əl kodunu yükləyə bilərsiniz.

Sonra onu mənbə kodunuzda istifadə edə bilərsiniz.

 var clone = require('clone'); var a = { foo: { bar: 'baz' } }; // inital value of a var b = clone(a); // clone a -> b a.foo.bar = 'foo'; // change a console.log(a); // { foo: { bar: 'foo' } } console.log(b); // { foo: { bar: 'baz' } } 

(Disclaimer: Kitabın müəllifi Im.)

48
17 окт. Cavab 17 ocaq verilir . 2012-10-17 21:36 '12 at 21:36 2012-10-17 21:36

Cloning Object həmişə JS-də narahatlıq doğururdu, ancaq ES6-dan əvvəl bütün bunlar bir JavaScript obyektinin kopyalanmasının müxtəlif yollarını qeyd edirəm, aşağıda bir obyektin olduğunu düşünün və bunun dərin kopyasını almaq istəsəniz:

 var obj = {a:1, b:2, c:3, d:4}; 

Kaynağı dəyişdirmədən bu obyektin surətini çıxarmanın bir neçə yolu var:

1) ES5 + kopyalamaq üçün sadə bir funksiyanı istifadə edərək:

 function deepCopyObj(obj) { if (null == obj || "object" != typeof obj) return obj; if (obj instanceof Date) { var copy = new Date(); copy.setTime(obj.getTime()); return copy; } if (obj instanceof Array) { var copy = []; for (var i = 0, len = obj.length; i < len; i++) { copy[i] = cloneSO(obj[i]); } return copy; } if (obj instanceof Object) { var copy = {}; for (var attr in obj) { if (obj.hasOwnProperty(attr)) copy[attr] = cloneSO(obj[attr]); } return copy; } throw new Error("Unable to copy obj this object."); } 

2) ES5 + JSON.parse və JSON.stringify istifadə edərək.

 var deepCopyObj = JSON.parse(JSON.stringify(obj)); 

3) AngularJs:

 var deepCopyObj = angular.copy(obj); 

4) jQuery:

 var deepCopyObj = jQuery.extend(true, {}, obj); 

5) UnderscoreJs və Loadash:

 var deepCopyObj = _.cloneDeep(obj); //latest version UndescoreJs makes shallow copy 

Bu kömək ümid edirik ...

47
03 апр. Cavab Alireza 03 Aprel verilir. 2017-04-03 18:37 '17 saat 18:37 'da 2017-04-03 18:37

Əgər onu istifadə etsəniz, Underscore.js kitabxanasında bir klon var .

 var newObject = _.clone(oldObject); 
46
15 дек. cavabı 15 dekabrda verilir. 2011-12-15 18:56 '11 'də 18:56' də 2011-12-15 18:56

Burada ConroyP-nin yuxarıdakı versiyasıdır. Dizayn parametrləri tələb olunsa belə:

 //If Object.create isn't already defined, we just do the simple shim, //without the second argument, since that all we need here var object_create = Object.create; if (typeof object_create !== 'function') { object_create = function(o) { function F() {} F.prototype = o; return new F(); }; } function deepCopy(obj) { if(obj == null || typeof(obj) !== 'object'){ return obj; } //make sure the returned object has the same prototype as the original var ret = object_create(obj.constructor.prototype); for(var key in obj){ ret[key] = deepCopy(obj[key]); } return ret; } 

Bu xüsusiyyət mənim sadə kitabxanamda da mövcuddur.

Düzenle:

İşdə daha etibarlı bir versiya (Justin McCandles sayəsində, hal-hazırda dairə istinadları dəstəkləyir):

  function deepCopy(src,  _visited, _copiesVisited) { if(src === null || typeof(src) !== 'object'){ return src; } //Honor native/custom clone methods if(typeof src.clone == 'function'){ return src.clone(true); } //Special cases: //Date if(src instanceof Date){ return new Date(src.getTime()); } //RegExp if(src instanceof RegExp){ return new RegExp(src); } //DOM Element if(src.nodeType  typeof src.cloneNode == 'function'){ return src.cloneNode(true); } // Initialize the visited objects arrays if needed. // This is used to detect cyclic references. if (_visited === undefined){ _visited = []; _copiesVisited = []; } // Check if this object has already been visited var i, len = _visited.length; for (i = 0; i < len; i++) { // If so, get the copy we already made if (src === _visited[i]) { return _copiesVisited[i]; } } //Array if (Object.prototype.toString.call(src) == '[object Array]') { //[].slice() by itself would soft clone var ret = src.slice(); //add it to the visited array _visited.push(src); _copiesVisited.push(ret); var i = ret.length; while (i--) { ret[i] = deepCopy(ret[i], _visited, _copiesVisited); } return ret; } //If we've reached here, we have a regular object //make sure the returned object has the same prototype as the original var proto = (Object.getPrototypeOf ? Object.getPrototypeOf(src): src.__proto__); if (!proto) { proto = src.constructor.prototype; //this line would probably only be reached by very old browsers } var dest = object_create(proto); //add this object to the visited array _visited.push(src); _copiesVisited.push(dest); for (var key in src) { //Note: this does NOT preserve ES5 property attributes like 'writable', 'enumerable', etc. //For an example of how this could be modified to do so, see the singleMixin() function dest[key] = deepCopy(src[key], _visited, _copiesVisited); } return dest; } //If Object.create isn't already defined, we just do the simple shim, //without the second argument, since that all we need here var object_create = Object.create; if (typeof object_create !== 'function') { object_create = function(o) { function F() {} F.prototype = o; return new F(); }; } 
34
11 нояб. Matt Browne tərəfindən verilmiş cavab 11 noyabr. 2012-11-11 20:53 '12 at 8:53 pm 2012-11-11 20:53

Aşağıdakılar eyni obyektin iki nümunəsini yaradır. Mən onu tapdım və indi istifadə edirəm. Sadə və istifadə üçün asandır.

 var objToCreate = JSON.parse(JSON.stringify(cloneThis)); 
30
21 авг. Nathan rogers tərəfindən verilmiş cavab 21 Avqust 2015-08-21 18:51 '15 at 18:51 'də yola salındı
 function clone(obj) { var clone = {}; clone.prototype = obj.prototype; for (property in obj) clone[property] = obj[property]; return clone; } 
22
23 сент. 23 sentyabr tarixində Mark Cidade tərəfindən verilmiş cavab 2008-09-23 19:45 '08 at 7:45 pm 2008-09-23 19:45

Lodaşda yaxşı bir metod var . cloneDeep (dəyər) :

 var objects = [{ 'a': 1 }, { 'b': 2 }]; var deep = _.cloneDeep(objects); console.log(deep[0] === objects[0]); // => false 
20
22 июня '13 в 18:03 2013-06-22 18:03 cavab 22-29 iyun 2013- cü il saat 18:03 tarixində açıqlanacaq

Crockford bu funksiyanı istifadə etmək üçün (və üstünlük verir) təklif edir:

 function object(o) { function F() {} F.prototype = o; return new F(); } var newObject = object(oldObject); 

Qısa, gözlənildiyi kimi işləyir və kitabxanaya ehtiyacınız yoxdur.


EDIT:

Bu, Object.create üçün bir polifillidir, belə ki, onu da istifadə edə bilərsiniz.

 var newObject = Object.create(oldObject); 

Qeyd. . Onlardan bəzilərini istifadə hasOwnProperty , hasOwnProperty istifadə edən bəzi iteration problemləri ola bilər. create , oldObject devraldıran yeni bir boş obyekt yaradır. Amma obyektlərin klonlanması üçün hələ də faydalı və praktikdir.

Məsələn, əgər oldObject.a = 5;

 newObject.a; // is 5 

a

 oldObject.hasOwnProperty(a); // is true newObject.hasOwnProperty(a); // is false 
20
06 окт. Cavab Chris Broski tərəfindən verilmiş Oct 06 2010-10-06 18:08 '10 at 18:08 2010-10-06 18:08

Bir qat ilə kiçik nüsxə ( 5-ci nəşr ECMAScript ):

 var origin = { foo : {} }; var copy = Object.keys(origin).reduce(function(c,k){c[k]=origin[k];return c;},{}); console.log(origin, copy); console.log(origin == copy); // false console.log(origin.foo == copy.foo); // true 

Və bir qat ilə kiçik bir surət ( ECMAScript 6th Edition , 2015):

 var origin = { foo : {} }; var copy = Object.assign({}, origin); console.log(origin, copy); console.log(origin == copy); // false console.log(origin.foo == copy.foo); // true 
18
05 июля '12 в 0:44 2012-07-05 00:44 cavab iyul ayının 5-də saat 12: 00-da saat 12 : 44-da malı nison tərəfindən verilir

Çünki Mən AngularJS görmədim və insanların bilmək istəyirəm ki, düşündüm ...

angular.copy həmçinin obyektləri və serialları dərindən çıxarmaq üçün bir üsul təmin edir.

15
14 мая '16 в 1:16 2016-05-14 01:16 cavab 14-27 may tarixində 1:16 2016-05-14 01:16 tarixində Dan Atkinson verilir

Prototipdə belə bir şeylər var

 newObject = Object.clone(myObject); 

Prototip sənədləri bu sayısız bir surət təşkil edir.

14
02 дек. cavab verildi erlando 02 Dek 2017-12-02 20:47 '17 at 8:47 pm 2017-12-02 20:47

Array tipli obyektlər üçün ideal bir dərin klonlama operatoru yoxdur. Aşağıdakı koddan görə bilərsiniz ki, jQuery John Resig klonlama qeyri-sayısal xassələri olan dizilərə çevrilməyən obyektlərə çevrilir və RegDwight JSON klonlama qeyri-nümerik xüsusiyyətləri ləğv edir. Aşağıdakı testlər bir neçə brauzerdə bu nöqtələri göstərir:

 function jQueryClone(obj) { return jQuery.extend(true, {}, obj) } function JSONClone(obj) { return JSON.parse(JSON.stringify(obj)) } var arrayLikeObj = [[1, "a", "b"], [2, "b", "a"]]; arrayLikeObj.names = ["m", "n", "o"]; var JSONCopy = JSONClone(arrayLikeObj); var jQueryCopy = jQueryClone(arrayLikeObj); alert("Is arrayLikeObj an array instance?" + (arrayLikeObj instanceof Array) + "\nIs the jQueryClone an array instance? " + (jQueryCopy instanceof Array) + "\nWhat are the arrayLikeObj names? " + arrayLikeObj.names + "\nAnd what are the JSONClone names? " + JSONCopy.names) 
14
17 окт. Cavab 17 oktyabr tarixində qeyd olunur. 2010-10-17 07:01 '10 at 07:01 2010-10-17 07:01

Sizin məqsədi "düz köhnə JavaScript obyektini" klonlamaq olub-olmamasına bağlı olaraq iki yaxşı cavabım var.

Əsl obyektin prototiplərinə istinad etmədən tam bir klon yaratmaq niyyətindəyik. Tam klon ilə maraqlanmıyorsanız, digər cavablar (Crockford şablonu) bəzi təmin Object.clone () routines bir çox istifadə edə bilərsiniz.

Для простых старых объектов JavaScript проверенный и правдивый способ клонирования объекта в современных условиях выполнения довольно просто:

 var clone = JSON.parse(JSON.stringify(obj)); 

Обратите внимание, что исходный объект должен быть чистым объектом JSON. Это означает, что все его вложенные свойства должны быть скалярами (например, логическими, строковыми, массивными, объектными и т.д.). Любые функции или специальные объекты, такие как RegExp или Date, не будут клонированы.

Это эффективно? Черт возьми. Мы пробовали все виды методов клонирования, и это работает лучше всего. Я уверен, что какой-нибудь ниндзя может вызвать более быстрый метод. Но я подозреваю, что мы говорим о предельных выигрышах.

Этот подход прост и прост в реализации. Оберните его в функцию удобства, и если вам действительно нужно выжать некоторый выигрыш, пойдите в более позднее время.

Теперь для не-простых объектов JavaScript нет простого ответа. На самом деле не может быть из-за динамического характера функций JavaScript и состояния внутреннего объекта. Глубокое клонирование структуры JSON с функциями внутри требует, чтобы вы воссоздали эти функции и их внутренний контекст. И JavaScript просто не имеет стандартного способа сделать это.

Правильный способ сделать это, опять же, с помощью метода удобства, который вы декларируете и повторно используете в своем коде. Метод удобства может быть наделен некоторым пониманием ваших собственных объектов, поэтому вы можете правильно воссоздать график в новом объекте.

Мы пишем наши собственные, но лучший общий подход, который я видел, рассматривается здесь:

http://davidwalsh.name/javascript-clone

Это правильная идея. Автор (Дэвид Уолш) прокомментировал клонирование обобщенных функций. Это то, что вы, возможно, захотите сделать, в зависимости от вашего варианта использования.

Основная идея заключается в том, что вам нужно специально обрабатывать создание ваших функций (или прототипных классов, если можно так выразиться) на основе каждого типа. Здесь он привел несколько примеров для RegExp и Date.

Это не только код, но и очень читаемый. Это довольно легко расширить.

Является ли это эффективным? Черт возьми. Учитывая, что цель состоит в том, чтобы создать настоящий клон с глубокой копией, вам придется ходить по членам графа исходного объекта. При таком подходе вы можете точно настроить, какие дочерние элементы обрабатывать и как вручную обрабатывать настраиваемые типы.

Итак, вы идете. Два подхода. На мой взгляд, обе эффективны.

13
ответ дан Michael Uzquiano 06 мая '13 в 23:45 2013-05-06 23:45

Это не самое эффективное решение, но оно делает то, что мне нужно. Простые тестовые примеры ниже...

 function clone(obj, clones) { // Makes a deep copy of 'obj'. Handles cyclic structures by // tracking cloned obj in the 'clones' parameter. Functions // are included, but not cloned. Functions members are cloned. var new_obj, already_cloned, t = typeof obj, i = 0, l, pair; clones = clones || []; if (obj === null) { return obj; } if (t === "object" || t === "function") { // check to see if we've already cloned obj for (i = 0, l = clones.length; i < l; i++) { pair = clones[i]; if (pair[0] === obj) { already_cloned = pair[1]; break; } } if (already_cloned) { return already_cloned; } else { if (t === "object") { // create new object new_obj = new obj.constructor(); } else { // Just use functions as is new_obj = obj; } clones.push([obj, new_obj]); // keep track of objects we've cloned for (key in obj) { // clone object members if (obj.hasOwnProperty(key)) { new_obj[key] = clone(obj[key], clones); } } } } return new_obj || obj; } 

Тест циклического массива...

 a = [] a.push("b", "c", a) aa = clone(a) aa === a //=> false aa[2] === a //=> false aa[2] === a[2] //=> false aa[2] === aa //=> true 

Функциональный тест...

 f = new Function fa = a ff = clone(f) ff === f //=> true ff.a === a //=> false 
11
ответ дан neatonk 03 апр. '11 в 5:08 2011-04-03 05:08

AngularJS

Хорошо, если вы используете angular, вы тоже можете это сделать

 var newObject = angular.copy(oldObject); 
9
ответ дан azerafati 14 сент. '16 в 16:26 2016-09-14 16:26

Я не согласен с ответом с наибольшим количеством голосов здесь . Рекурсивный глубокий клон быстрее , чем упомянутый подход JSON.parse(JSON.stringify(obj)).

И здесь функция для быстрой справки:

 function cloneDeep (o) { let newO let i if (typeof o !== 'object') return o if (!o) return o if (Object.prototype.toString.apply(o) === '[object Array]') { newO = [] for (i = 0; i < o.length; i += 1) { newO[i] = cloneDeep(o[i]) } return newO } newO = {} for (i in o) { if (o.hasOwnProperty(i)) { newO[i] = cloneDeep(o[i]) } } return newO } 
9
ответ дан prograhammer 18 июня '17 в 9:34 2017-06-18 09:34
 // obj target object, vals source object var setVals = function (obj, vals) { if (obj  vals) { for (var x in vals) { if (vals.hasOwnProperty(x)) { if (obj[x]  typeof vals[x] === 'object') { obj[x] = setVals(obj[x], vals[x]); } else { obj[x] = vals[x]; } } } } return obj; }; 
9
ответ дан Dima 28 апр. '10 в 14:16 2010-04-28 14:16

Для людей, которые хотят использовать версию JSON.parse(JSON.stringify(obj)) , но без потери объектов Date, вы можете использовать второй аргумент метода parse , чтобы преобразовать строки в Date:

 function clone(obj) { var regExp = /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d{3}Z$/; return JSON.parse(JSON.stringify(x), function(k, v) { if (typeof v === 'string'  regExp.test(v)) return new Date(v); return v; }); } 
7
ответ дан Buzinas 29 окт. '15 в 19:09 2015-10-29 19:09

Вот комплексный метод clone(), который может клонировать любой объект JavaScript. Он обрабатывает почти все случаи:

 function clone(src, deep) { var toString = Object.prototype.toString; if (!src  typeof src != "object") { // Any non-object (Boolean, String, Number), null, undefined, NaN return src; } // Honor native/custom clone methods if (src.clone  toString.call(src.clone) == "[object Function]") { return src.clone(deep); } // DOM elements if (src.nodeType  toString.call(src.cloneNode) == "[object Function]") { return src.cloneNode(deep); } // Date if (toString.call(src) == "[object Date]") { return new Date(src.getTime()); } // RegExp if (toString.call(src) == "[object RegExp]") { return new RegExp(src); } // Function if (toString.call(src) == "[object Function]") { //Wrap in another method to make sure == is not true; //Note: Huge performance issue due to closures, comment this :) return (function(){ src.apply(this, arguments); }); } var ret, index; //Array if (toString.call(src) == "[object Array]") { //[].slice(0) would soft clone ret = src.slice(); if (deep) { index = ret.length; while (index--) { ret[index] = clone(ret[index], true); } } } //Object else { ret = src.constructor ? new src.constructor() : {}; for (var prop in src) { ret[prop] = deep ? clone(src[prop], true) : src[prop]; } } return ret; }; 
7
ответ дан user1547016 24 июля '12 в 0:39 2012-07-24 00:39
  • 1
  • 2
  • 3

Другие вопросы по меткам или Задайте вопрос