मैं जावास्क्रिप्ट ऑब्जेक्ट को सही ढंग से कैसे क्लोन करूं?


3075

मेरे पास एक वस्तु है x। मैं इसे ऑब्जेक्ट के रूप में कॉपी करना चाहता हूं y, जैसे कि परिवर्तन yनहीं करना x। मैंने महसूस किया कि अंतर्निहित जावास्क्रिप्ट वस्तुओं से प्राप्त वस्तुओं की प्रतिलिपि बनाने से अतिरिक्त, अवांछित गुण हो जाएंगे। यह एक समस्या नहीं है, क्योंकि मैं अपनी खुद की शाब्दिक-निर्मित वस्तुओं की नकल कर रहा हूं।

मैं जावास्क्रिप्ट ऑब्जेक्ट को सही ढंग से कैसे क्लोन करूं?


30
इस प्रश्न को देखें: stackoverflow.com/questions/122102/…
नियाज़

256
JSON के लिए, मैं उपयोग करता हूंmObj=JSON.parse(JSON.stringify(jsonObject));
लॉर्ड लोह।

67
मुझे वास्तव में नहीं मिलता है कि कोई भी क्यों नहीं सुझाव देता है Object.create(o), यह वह सब कुछ करता है जो लेखक पूछता है?
Froginvasion

43
var x = { deep: { key: 1 } }; var y = Object.create(x); x.deep.key = 2; ऐसा करने के बाद, y.deep.key2 भी होंगे, इसलिए Object.create क्लोनिंग के लिए इस्तेमाल नहीं किया जा सकता है ...
रुबेन स्टोक

17
@ r3wt जो काम नहीं करेगा ... कृपया समाधान के मूल परीक्षण करने के बाद ही पोस्ट करें ..
अक्षय

जवाबों:


1560

जावास्क्रिप्ट में किसी भी वस्तु के लिए ऐसा करना सरल या सीधा नहीं होगा। आप ऑब्जेक्ट के प्रोटोटाइप से गलत तरीके से विशेषताओं को चुनने की समस्या में भाग लेंगे, जिसे प्रोटोटाइप में छोड़ दिया जाना चाहिए और नए उदाहरण में कॉपी नहीं किया जाना चाहिए। यदि, उदाहरण के लिए, आप एक cloneविधि जोड़ रहे हैं, तो Object.prototypeकुछ उत्तरों के रूप में, आपको स्पष्ट रूप से उस विशेषता को छोड़ना होगा। लेकिन क्या होगा अगर वहाँ अन्य अतिरिक्त तरीकों को जोड़ा जाए Object.prototype, या अन्य मध्यवर्ती प्रोटोटाइप, जिनके बारे में आप नहीं जानते हैं? उस स्थिति में, आप उन विशेषताओं को कॉपी करेंगे, जिन्हें आपको नहीं करना चाहिए, इसलिए आपको hasOwnPropertyविधि के साथ अप्रत्याशित, गैर-स्थानीय विशेषताओं का पता लगाने की आवश्यकता है ।

गैर-विलुप्त होने वाली विशेषताओं के अलावा, जब आप उन वस्तुओं को कॉपी करने का प्रयास करेंगे, जिनमें छिपी हुई संपत्ति हैं, तो आप एक कठिन समस्या का सामना करेंगे। उदाहरण के लिए, prototypeएक फ़ंक्शन की छिपी हुई संपत्ति है। इसके अलावा, एक ऑब्जेक्ट के प्रोटोटाइप को विशेषता के साथ संदर्भित किया जाता है __proto__, जो कि छिपा हुआ भी है, और स्रोत ऑब्जेक्ट की विशेषताओं पर पुनरावृति / लूप में for / द्वारा कॉपी नहीं किया जाएगा। मुझे लगता है कि __proto__फ़ायरफ़ॉक्स जावास्क्रिप्ट दुभाषिया के लिए विशिष्ट हो सकता है और यह अन्य ब्राउज़रों में कुछ अलग हो सकता है, लेकिन आपको चित्र मिलता है। सब कुछ असंख्य नहीं है। यदि आप इसका नाम जानते हैं, तो आप किसी छिपी हुई विशेषता को कॉपी कर सकते हैं, लेकिन मुझे इसे स्वचालित रूप से खोजने का कोई तरीका नहीं पता है।

फिर भी एक सुरुचिपूर्ण समाधान की तलाश में एक और रोड़ा प्रोटोटाइप विरासत को सही ढंग से स्थापित करने की समस्या है। यदि आपके स्रोत ऑब्जेक्ट का प्रोटोटाइप है Object, तो बस एक नई सामान्य वस्तु के साथ {}काम करना होगा, लेकिन अगर स्रोत का प्रोटोटाइप कुछ वंशज है Object, तो आप उस प्रोटोटाइप से अतिरिक्त सदस्यों को गायब करने जा रहे हैं जिसे आपने hasOwnPropertyफ़िल्टर का उपयोग करके छोड़ दिया है , या जो प्रोटोटाइप में थे, लेकिन पहली जगह में नहीं थे। constructorप्रारंभिक प्रतिलिपि ऑब्जेक्ट को प्राप्त करने के लिए स्रोत ऑब्जेक्ट की संपत्ति को कॉल करने और उसके बाद गुणों पर प्रतिलिपि बनाने के लिए एक समाधान हो सकता है , लेकिन फिर भी आपको गैर-गणना योग्य विशेषताएं नहीं मिलेंगी। उदाहरण के लिए, एक Dateऑब्जेक्ट अपने डेटा को एक छिपे हुए सदस्य के रूप में संग्रहीत करता है:

function clone(obj) {
    if (null == obj || "object" != typeof obj) return obj;
    var copy = obj.constructor();
    for (var attr in obj) {
        if (obj.hasOwnProperty(attr)) copy[attr] = obj[attr];
    }
    return copy;
}

var d1 = new Date();

/* Executes function after 5 seconds. */
setTimeout(function(){
    var d2 = clone(d1);
    alert("d1 = " + d1.toString() + "\nd2 = " + d2.toString());
}, 5000);

इसके लिए दिनांक स्ट्रिंग d15 सेकंड पीछे होगी d2। एक विधि को दूसरे Dateके समान बनाने का एक setTimeतरीका है, लेकिन यह Dateवर्ग के लिए विशिष्ट है । मुझे नहीं लगता कि इस समस्या का कोई बुलेट-प्रूफ सामान्य समाधान है, हालांकि मुझे गलत होने की खुशी होगी!

जब मैं कॉपी करने मैं यह सोचते हैं कि मैं केवल कॉपी करने के लिए एक सादे आवश्यकता होगी द्वारा समझौता समाप्त हो गया सामान्य गहरी लागू करने के लिए किया था Object, Array, Date, String, Number, या Boolean। अंतिम 3 प्रकार अपरिवर्तनीय हैं, इसलिए मैं उथली प्रतिलिपि बना सकता हूं और इसे बदलने की चिंता नहीं कर सकता। मैं आगे मान लिया है कि किसी भी तत्व में निहित Objectया Arrayभी उस सूची में 6 सरल प्रकारों में से एक होगा। यह निम्नलिखित की तरह कोड के साथ पूरा किया जा सकता है:

function clone(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] = clone(obj[i]);
        }
        return copy;
    }

    // Handle Object
    if (obj instanceof Object) {
        copy = {};
        for (var attr in obj) {
            if (obj.hasOwnProperty(attr)) copy[attr] = clone(obj[attr]);
        }
        return copy;
    }

    throw new Error("Unable to copy obj! Its type isn't supported.");
}

उपरोक्त फ़ंक्शन मेरे द्वारा उल्लिखित 6 सरल प्रकारों के लिए पर्याप्त रूप से काम करेगा, जब तक कि वस्तुओं और सरणियों में डेटा एक पेड़ की संरचना बनाते हैं। अर्थात्, ऑब्जेक्ट में एक ही डेटा का एक से अधिक संदर्भ नहीं है। उदाहरण के लिए:

// This would be cloneable:
var tree = {
    "left"  : { "left" : null, "right" : null, "data" : 3 },
    "right" : null,
    "data"  : 8
};

// This would kind-of work, but you would get 2 copies of the 
// inner node instead of 2 references to the same copy
var directedAcylicGraph = {
    "left"  : { "left" : null, "right" : null, "data" : 3 },
    "data"  : 8
};
directedAcyclicGraph["right"] = directedAcyclicGraph["left"];

// Cloning this would cause a stack overflow due to infinite recursion:
var cyclicGraph = {
    "left"  : { "left" : null, "right" : null, "data" : 3 },
    "data"  : 8
};
cyclicGraph["right"] = cyclicGraph;

यह किसी भी जावास्क्रिप्ट ऑब्जेक्ट को संभालने में सक्षम नहीं होगा, लेकिन यह कई उद्देश्यों के लिए पर्याप्त हो सकता है जब तक आप यह नहीं मानते कि यह आपके द्वारा फेंके गए किसी भी चीज के लिए काम करेगा।


5
एक नोडज में लगभग ठीक काम किया - बस के लिए लाइन को बदलना था (var i = 0, var len = ob.length; मैं <len; ++ i) {के लिए (var i = 0; i <obj.length; ++ i) {
ट्रिंडाज़

5
भविष्य के गोगलर्स के लिए: एक ही गहरी प्रति, gist.github.com/2234277
Trindaz

7
आजकल JSON.parse(JSON.stringify([some object]),[some revirer function])एक समाधान होगा?
KooiInc

5
पहले स्निपेट में, क्या आप सुनिश्चित हैं कि यह नहीं होना चाहिए var cpy = new obj.constructor()?
साइबर

8
ऑब्जेक्ट असाइन करना Chrome में एक सच्ची प्रतिलिपि बनाने के लिए प्रतीत नहीं होता है, मूल ऑब्जेक्ट के संदर्भ को बनाए रखता है - JSON.stringify और JSON.parse का उपयोग करके समाप्त हो गया है - पूरी तरह से काम किया
1owk3y

974

यदि आप Dateअपनी वस्तु के भीतर s, फ़ंक्शन, अपरिभाषित, regExp या इन्फिनिटी का उपयोग नहीं करते हैं , तो एक बहुत ही सरल एक लाइनर है JSON.parse(JSON.stringify(object)):

const a = {
  string: 'string',
  number: 123,
  bool: false,
  nul: null,
  date: new Date(),  // stringified
  undef: undefined,  // lost
  inf: Infinity,  // forced to 'null'
}
console.log(a);
console.log(typeof a.date);  // Date object
const clone = JSON.parse(JSON.stringify(a));
console.log(clone);
console.log(typeof clone.date);  // result of .toISOString()

यह सभी प्रकार के ऑब्जेक्ट्स, एरेज़, स्ट्रिंग्स, बुलियन और संख्याओं के लिए काम करता है।

ब्राउज़रों के संरचित क्लोन एल्गोरिथ्म के बारे में भी इस लेख को देखें जो किसी कार्यकर्ता को संदेश पोस्ट करते समय उपयोग किया जाता है। इसमें गहरी क्लोनिंग के लिए एक फ़ंक्शन भी शामिल है।


42
ध्यान दें कि यह केवल परीक्षण के लिए उपयोग किया जा सकता है। सबसे पहले, यह समय और स्मृति खपत के संदर्भ में इष्टतम से बहुत दूर है। दूसरे, सभी ब्राउज़रों में यह विधियाँ नहीं हैं।
नक्स

2
आप हमेशा JSON2.js या JSON3.js शामिल कर सकते हैं। आपको अपने ऐप के लिए वैसे भी उनकी आवश्यकता होगी। लेकिन मैं मानता हूं कि यह सबसे अच्छा समाधान नहीं हो सकता है, क्योंकि JSON.stringify में विरासत में मिली संपत्ति शामिल नहीं है।
टिम हांग

78
@ Nux, समय और स्मृति के संदर्भ में इष्टतम क्यों नहीं है? मिज़ीन कहता है: "उथली नक़ल (गहरी वस्तु पर) की तुलना में यह विधि धीमी है। यह विधि परिभाषा के अनुसार, गहरी प्रतियों की है। लेकिन चूँकि JSON मूल कोड (अधिकांश ब्राउज़रों में) पर लागू होता है, इसलिए यह काफी तेज़ होगा। किसी भी अन्य जावास्क्रिप्ट-आधारित गहरी प्रतिलिपि समाधान का उपयोग करने की तुलना में, और कभी-कभी एक जावास्क्रिप्ट-आधारित उथली नकल तकनीक की तुलना में तेज हो सकती है (देखें: jsperf.com/cloning-an-object/79)। " stackoverflow.com/questions/122102/…
BeauCielBleu

16
मैं सिर्फ इसके लिए अक्टूबर 2014 तक एक अपडेट जोड़ना चाहता हूं। क्रोम 37+ JSON.parse (JSON.stringify (oldObject)) के साथ तेज है; इसका उपयोग करने का लाभ यह है कि जावास्क्रिप्ट इंजन को देखने और कुछ बेहतर करने के लिए अनुकूलित करना बहुत आसान है यदि वह चाहता है।
मृगांक

17
२०१६ अपडेट: यह अब हर ब्राउज़र में व्यापक रूप से उपयोग किए जा रहे काम करना चाहिए। (देखें क्या मैं उपयोग कर सकता हूं ... ) अब मुख्य सवाल यह होगा कि क्या यह पर्याप्त रूप से प्रदर्शन करने वाला है।
जेम्स फोस्टर

783

JQuery के साथ, आप कर सकते हैं उथले प्रतिलिपि के साथ विस्तार :

var copiedObject = jQuery.extend({}, originalObject)

copiedObjectवसीयत में बाद के बदलावों का असर नहीं होगा originalObject, और इसके विपरीत।

या एक गहरी प्रतिलिपि बनाने के लिए :

var copiedObject = jQuery.extend(true, {}, originalObject)

164
या यहाँ तक कि:var copiedObject = jQuery.extend({},originalObject);
अनुदान मैकलीन

82
गहरी प्रति के लिए पहला परम के रूप में सच निर्दिष्ट करने के लिए भी उपयोगी है:jQuery.extend(true, {}, originalObject);
विल शेवर

6
हां, मुझे यह लिंक सहायक (पास्कल के समान समाधान) stackoverflow.com/questions/122102/…
गैरी इंग्लिश

3
बस एक ध्यान दें, यह मूल वस्तु के प्रोटो कंस्ट्रक्टर की नकल नहीं करता है
सैम जोन्स

1
के अनुसार stackoverflow.com/questions/184710/... , ऐसा लगता है कि "उथले प्रतिलिपि" बस originalObject के संदर्भ कॉपी, तो क्यों कि यहाँ कहते हैं ...subsequent changes to the copiedObject will not affect the originalObject, and vice versa...। क्षमा करें कि मैं वास्तव में भ्रमित था।
कार्टर

684

ECMAScript 6 में Object.assign पद्धति है, जो एक वस्तु से दूसरी वस्तु के सभी असंख्य गुणों को मानती है । उदाहरण के लिए:

var x = {myProp: "value"};
var y = Object.assign({}, x); 

लेकिन ध्यान रखें कि नेस्टेड वस्तुओं को अभी भी संदर्भ के रूप में कॉपी किया जाता है।


हाँ, मुझे विश्वास है कि Object.assignजाने का रास्ता है। इसे पॉलीफ़िल

22
इसके अलावा ध्यान रखें कि यह "तरीकों" वस्तु शाब्दिक के माध्यम से परिभाषित पर कॉपी जाएगा (के बाद से इन कर रहे हैं लेकिन गणनीय) नहीं "वर्ग" तंत्र के माध्यम से ललकारा विधियों (के बाद से इन नहीं हैं गणनीय)।
मार्कस जूनियस ब्रूटस

16
मुझे लगता है कि यह उल्लेख किया जाना चाहिए कि यह एज को छोड़कर एज द्वारा नहीं है। कुछ लोग अभी भी इसका उपयोग करते हैं।
सौलियस

1
यह @EugeneTiurin उनके उत्तर के समान है।
विल्ट

5
यहाँ अनुभव से बोलते हुए ... यह प्रयोग न करें। ऑब्जेक्ट्स को पदानुक्रम के लिए डिज़ाइन किया गया है और आप केवल पहले स्तर की नकल करेंगे, जिससे आप पूरी वस्तु की प्रतिलिपि बना सकते हैं। मेरा विश्वास करो, यह आपको सिर खुजलाने के दिनों को बचा सकता है।
Ow3n

232

प्रति एमडीएन :

  • यदि आप उथली प्रतिलिपि चाहते हैं, तो उपयोग करें Object.assign({}, a)
  • "गहरी" प्रतिलिपि के लिए, उपयोग करें JSON.parse(JSON.stringify(a))

बाहरी पुस्तकालयों की कोई आवश्यकता नहीं है, लेकिन आपको पहले ब्राउज़र संगतता की जांच करने की आवश्यकता है ।


8
JSON.parse (JSON.stringify (a)) सुंदर दिखता है, लेकिन इसका उपयोग करने से पहले मैं आपके इच्छित संग्रह के लिए समय निर्धारित करने की सलाह देता हूं। ऑब्जेक्ट आकार के आधार पर, यह सबसे तेज विकल्प नहीं हो सकता है।
एदज़ा

3
JSON विधि मैंने देखा कि तारीख वस्तुओं को तार में नहीं बल्कि तारीखों में परिवर्तित करता है। जावास्क्रिप्ट में समय-ज़ोन के मज़े से निपटना है और किसी भी तारीख को मैन्युअल रूप से ठीक करना है। तारीखों के अलावा अन्य प्रकारों के लिए भी इसी तरह के मामले हो सकते हैं
स्टीव सीजर

तिथि के लिए, मैं गति का उपयोग cloneकरता हूँ। क्योंकि यह कार्यक्षमता है। यहाँ और अधिक देखें क्षणिकाएँ
तारेक

यह अच्छा है लेकिन ध्यान रखें कि वस्तु के अंदर कार्य हों
lesolorzanov

जोंस पार्सिंग के साथ एक और मुद्दा परिपत्र संदर्भ है। यदि वस्तु के अंदर कोई वृत्ताकार सन्दर्भ हैं, तो यह टूट जाएगा
दार्शनिक

133

कई उत्तर हैं, लेकिन कोई भी जिसका उल्लेख ECMAScript 5 से Object.create में नहीं किया गया है, जो वास्तव में आपको एक सटीक प्रतिलिपि नहीं देता है, लेकिन स्रोत को नए ऑब्जेक्ट के प्रोटोटाइप के रूप में सेट करता है।

इस प्रकार, यह प्रश्न का सटीक उत्तर नहीं है, लेकिन यह एक-पंक्ति समाधान है और इस प्रकार सुरुचिपूर्ण है। और यह 2 मामलों के लिए सबसे अच्छा काम करता है:

  1. जहां इस तरह की विरासत उपयोगी है (डुह!)
  2. जहां स्रोत ऑब्जेक्ट को संशोधित नहीं किया जाएगा, इस प्रकार 2 वस्तुओं के बीच संबंध को एक गैर मुद्दा बना देता है।

उदाहरण:

var foo = { a : 1 };
var bar = Object.create(foo);
foo.a; // 1
bar.a; // 1
foo.a = 2;
bar.a; // 2 - prototype changed
bar.a = 3;
foo.a; // Still 2, since setting bar.a makes it an "own" property

मैं इस समाधान को श्रेष्ठ क्यों मानता हूं? यह मूल है, इस प्रकार कोई पाशन, कोई पुनरावृत्ति नहीं है। हालांकि, पुराने ब्राउज़रों को पॉलीफिल की आवश्यकता होगी।


नोट: Object.create एक गहरी प्रतिलिपि नहीं है (itpastorn उल्लेख नहीं पुनरावृत्ति)। प्रमाण:var a = {b:'hello',c:{d:'world'}}, b = Object.create(a); a == b /* false */; a.c == b.c /* true */;
zamnuts

103
यह प्रोटोटाइप इनहेरिटेंस है, क्लोनिंग नहीं। ये पूरी तरह से अलग चीजें हैं। नई वस्तु का अपना कोई गुण नहीं है, यह सिर्फ प्रोटोटाइप के गुणों की ओर इशारा करता है। क्लोनिंग का बिंदु एक नई नई वस्तु बनाना है जो किसी भी गुण को किसी अन्य ऑब्जेक्ट में संदर्भित नहीं करता है।
d13

7
मैं पूरी तरह से आपके साथ सहमत हूं। मैं इस बात से भी सहमत हूं कि यह क्लोनिंग नहीं है जैसा कि 'इरादा' हो सकता है। लेकिन लोगों पर आते हैं, अस्पष्ट समाधान खोजने की कोशिश करने के बजाय जावास्क्रिप्ट की प्रकृति को गले लगाते हैं जो मानकीकृत नहीं हैं। निश्चित रूप से, आप प्रोटोटाइप पसंद नहीं करते हैं और वे सभी "ब्ला" हैं, लेकिन वे वास्तव में बहुत उपयोगी हैं यदि आप जानते हैं कि आप क्या कर रहे हैं।
मेंढ़क

4
@RobG: यह लेख संदर्भित और क्लोनिंग के बीच का अंतर बताता है: en.wikipedia.org/wiki/Cloning_(programming)Object.createसंदर्भ के माध्यम से माता-पिता के गुणों को इंगित करता है। इसका मतलब है कि अगर माता-पिता की संपत्ति के मूल्य बदलते हैं, तो बच्चे की इच्छा भी बदल जाएगी। यह नेस्टेड सरणियों और वस्तुओं के साथ कुछ आश्चर्यजनक साइड-इफेक्ट्स हैं जो आपके कोड में हार्ड-टू- फाइंड बग्स को जन्म दे सकते हैं यदि आपको उनके बारे में पता नहीं है: jsbin.com/EKivInO/2 । एक क्लोन ऑब्जेक्ट पूरी तरह से नया, स्वतंत्र ऑब्जेक्ट है जिसमें माता-पिता के समान गुण और मूल्य हैं, लेकिन माता-पिता से जुड़ा नहीं है।
d13

1
यह लोगों को गुमराह करता है ... Object.create () का उपयोग वंशानुक्रम के लिए एक साधन के रूप में किया जा सकता है, लेकिन क्लोनिंग इसके पास कहीं नहीं है।
प्रज्ञावन्था

128

कोड की एक पंक्ति में एक जावास्क्रिप्ट ऑब्जेक्ट क्लोन करने का एक सुंदर तरीका

एक Object.assignविधि ECMAScript 2015 (ES6) मानक का हिस्सा है और ठीक वही है जो आपको चाहिए।

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

Object.assign () विधि का उपयोग एक या एक से अधिक स्रोत ऑब्जेक्ट से लक्ष्य सामग्री के सभी गणना करने योग्य गुणों के मूल्यों को कॉपी करने के लिए किया जाता है।

अधिक पढ़ें...

Polyfill पुराने ब्राउज़र का समर्थन करने के:

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

गूंगे प्रश्न के लिए क्षमा करें, लेकिन पॉलीफ़िल में फ़ंक्शन केवल एक पैरामीटर Object.assignलेने पर दो पैरामीटर क्यों valueलेता है?
क्वर्टी

@Qwertie कल सभी तर्क पुनरावृत्त होते हैं और एक वस्तु में विलीन हो जाते हैं, पिछले पास किए गए गुण से गुण को प्राथमिकता देते हैं
यूजीन ट्यूरिन

ओह, मैं देख रहा हूँ, धन्यवाद (मैं argumentsपहले वस्तु से परिचित नहीं था ।) मुझे Object()Google के माध्यम से खोजने में परेशानी हो रही है ... यह एक टाइपकास्ट है, है ना?
क्वर्टी

44
यह केवल एक उथले "क्लोनिंग" का प्रदर्शन करेगा
मार्कस जुनियस ब्रूटस

1
यह उत्तर ठीक उसी के समान है: stackoverflow.com/questions/122102/… मुझे पता है कि यह उसी व्यक्ति द्वारा है, लेकिन आपको केवल उत्तर की प्रतिलिपि बनाने के बजाय संदर्भ देना चाहिए।
१२:19

86

इंटरनेट पर अधिकांश समाधानों के साथ कई समस्याएं हैं। इसलिए मैंने एक अनुवर्ती बनाने का फैसला किया, जिसमें शामिल है, क्यों स्वीकार किए गए उत्तर को स्वीकार नहीं किया जाना चाहिए।

प्रारंभिक स्थिति

मैं अपने सभी बच्चों और उनके बच्चों के साथ एक जावास्क्रिप्ट कॉपी करना चाहता हूं Object। लेकिन जब से मैं एक सामान्य डेवलपर की तरह नहीं हूं, मेरे Objectपास सामान्य है properties , circular structuresऔर यहां तक ​​कि nested objects

तो चलो एक circular structureऔर एक nested objectपहले बनाएँ ।

function Circ() {
    this.me = this;
}

function Nested(y) {
    this.y = y;
}

चलो एक Objectनाम में सब कुछ एक साथ ले आओ a

var a = {
    x: 'a',
    circ: new Circ(),
    nested: new Nested('a')
};

अगला, हम aनाम के एक चर में कॉपी करना चाहते हैं bऔर इसे बदलना चाहते हैं।

var b = a;

b.x = 'b';
b.nested.y = 'b';

आप जानते हैं कि यहां क्या हुआ क्योंकि अगर आप इस महान प्रश्न पर भी नहीं उतरेंगे।

console.log(a, b);

a --> Object {
    x: "b",
    circ: Circ {
        me: Circ { ... }
    },
    nested: Nested {
        y: "b"
    }
}

b --> Object {
    x: "b",
    circ: Circ {
        me: Circ { ... }
    },
    nested: Nested {
        y: "b"
    }
}

अब इसका कोई हल निकालते हैं।

JSON

मैंने जो पहला प्रयास किया वह प्रयोग कर रहा था JSON

var b = JSON.parse( JSON.stringify( a ) );

b.x = 'b';
b.nested.y = 'b';

उस पर बहुत समय बर्बाद मत करो, आप प्राप्त करेंगे TypeError: Converting circular structure to JSON

पुनरावर्ती प्रतिलिपि (स्वीकृत "उत्तर")

आइए एक नजर डालते हैं स्वीकृत उत्तर पर।

function cloneSO(obj) {
    // Handle the 3 simple types, and null or undefined
    if (null == obj || "object" != typeof obj) return obj;

    // Handle Date
    if (obj instanceof Date) {
        var copy = new Date();
        copy.setTime(obj.getTime());
        return copy;
    }

    // Handle Array
    if (obj instanceof Array) {
        var copy = [];
        for (var i = 0, len = obj.length; i < len; i++) {
            copy[i] = cloneSO(obj[i]);
        }
        return copy;
    }

    // Handle Object
    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! Its type isn't supported.");
}

अच्छा लग रहा है, हे? यह ऑब्जेक्ट की पुनरावर्ती प्रतिलिपि है और अन्य प्रकारों को भी संभालता है, जैसे Date, लेकिन यह एक आवश्यकता नहीं थी।

var b = cloneSO(a);

b.x = 'b';
b.nested.y = 'b';

पुनरावृत्ति और circular structuresएक साथ अच्छी तरह से काम नहीं करता है ...RangeError: Maximum call stack size exceeded

मूल समाधान

मेरे सहकर्मी के साथ बहस करने के बाद, मेरे बॉस ने हमसे पूछा कि क्या हुआ, और उन्होंने कुछ गुगली करने के बाद एक सरल समाधान पाया । इसे कहते हैं Object.create

var b = Object.create(a);

b.x = 'b';
b.nested.y = 'b';

यह समाधान कुछ समय पहले जावास्क्रिप्ट में जोड़ा गया था और यहां तक ​​कि हैंडल भी circular structure

console.log(a, b);

a --> Object {
    x: "a",
    circ: Circ {
        me: Circ { ... }
    },
    nested: Nested {
        y: "b"
    }
}

b --> Object {
    x: "b",
    circ: Circ {
        me: Circ { ... }
    },
    nested: Nested {
        y: "b"
    }
}

... और आप देखते हैं, यह नेस्टेड संरचना के साथ काम नहीं किया।

मूल समाधान के लिए पॉलीफिल

Object.createIE 8 की तरह पुराने ब्राउज़र में पॉलीफ़िल है। यह मोज़िला द्वारा अनुशंसित कुछ ऐसा है, और निश्चित रूप से, यह सही नहीं है और मूल समाधान के समान समस्या का परिणाम है

function F() {};
function clonePF(o) {
    F.prototype = o;
    return new F();
}

var b = clonePF(a);

b.x = 'b';
b.nested.y = 'b';

मैंने Fदायरे से बाहर कर दिया है ताकि हम जो कुछ भी instanceofबताते हैं उस पर एक नज़र डाल सकें ।

console.log(a, b);

a --> Object {
    x: "a",
    circ: Circ {
        me: Circ { ... }
    },
    nested: Nested {
        y: "b"
    }
}

b --> F {
    x: "b",
    circ: Circ {
        me: Circ { ... }
    },
    nested: Nested {
        y: "b"
    }
}

console.log(typeof a, typeof b);

a --> object
b --> object

console.log(a instanceof Object, b instanceof Object);

a --> true
b --> true

console.log(a instanceof F, b instanceof F);

a --> false
b --> true

देशी समाधान के रूप में एक ही समस्या है , लेकिन थोड़ा बदतर उत्पादन।

बेहतर (लेकिन सही नहीं) समाधान

जब चारों ओर खुदाई करते हैं, तो मुझे एक समान प्रश्न मिला ( जावास्क्रिप्ट में, जब एक गहरी प्रतिलिपि का प्रदर्शन करते हैं, तो मैं एक चक्र से कैसे बच सकता हूं, क्योंकि यह एक संपत्ति "इस"? ) के कारण है, लेकिन एक तरह से बेहतर समाधान है।

function cloneDR(o) {
    const gdcc = "__getDeepCircularCopy__";
    if (o !== Object(o)) {
        return o; // primitive value
    }

    var set = gdcc in o,
        cache = o[gdcc],
        result;
    if (set && typeof cache == "function") {
        return cache();
    }
    // else
    o[gdcc] = function() { return result; }; // overwrite
    if (o instanceof Array) {
        result = [];
        for (var i=0; i<o.length; i++) {
            result[i] = cloneDR(o[i]);
        }
    } else {
        result = {};
        for (var prop in o)
            if (prop != gdcc)
                result[prop] = cloneDR(o[prop]);
            else if (set)
                result[prop] = cloneDR(cache);
    }
    if (set) {
        o[gdcc] = cache; // reset
    } else {
        delete o[gdcc]; // unset again
    }
    return result;
}

var b = cloneDR(a);

b.x = 'b';
b.nested.y = 'b';

और आइए नजर डालते हैं आउटपुट पर ...

console.log(a, b);

a --> Object {
    x: "a",
    circ: Object {
        me: Object { ... }
    },
    nested: Object {
        y: "a"
    }
}

b --> Object {
    x: "b",
    circ: Object {
        me: Object { ... }
    },
    nested: Object {
        y: "b"
    }
}

console.log(typeof a, typeof b);

a --> object
b --> object

console.log(a instanceof Object, b instanceof Object);

a --> true
b --> true

console.log(a instanceof F, b instanceof F);

a --> false
b --> false

आवश्यकताओं मिलान किया जाता है, लेकिन अभी भी कुछ छोटे मुद्दों, बदलना भी शामिल हैं instanceकी nestedऔर circकरने के लिए Object

पत्तों को साझा करने वाले पेड़ों की संरचना की नकल नहीं की जाएगी, वे दो स्वतंत्र पत्ते बन जाएंगे:

        [Object]                     [Object]
         /    \                       /    \
        /      \                     /      \
      |/_      _\|                 |/_      _\|  
  [Object]    [Object]   ===>  [Object]    [Object]
       \        /                 |           |
        \      /                  |           |
        _\|  |/_                 \|/         \|/
        [Object]               [Object]    [Object]

निष्कर्ष

पुनरावृत्ति और कैश का उपयोग करते हुए अंतिम समाधान, सबसे अच्छा नहीं हो सकता है, लेकिन यह वस्तु की वास्तविक गहरी प्रतिलिपि है। यह सरल है properties, circular structuresऔर nested object, लेकिन यह क्लोनिंग करते समय उनके उदाहरण को गड़बड़ कर देगा।

jsfiddle


11
इसलिए शंखनाद उस समस्या से बचने के लिए है :)
मिकस

@mikus जब तक एक वास्तविक विनिर्देश है जो केवल मूल उपयोग के मामलों से अधिक है, हाँ।
फैबियो पोलोनी

2
ऊपर दिए गए समाधानों का एक ठीक विश्लेषण लेकिन लेखक द्वारा निकाला गया निष्कर्ष बताता है कि इस प्रश्न का कोई हल नहीं है।
अमीर मोग

2
यह शर्म की बात है कि जेएस में देशी क्लोन फ़ंक्शन शामिल नहीं है।
l00k

1
सभी शीर्ष उत्तरों के बीच, मुझे लगता है कि यह सही के करीब है।
केटीयू

77

यदि आप उथली प्रति के साथ ठीक हैं, तो underscore.js लाइब्रेरी में क्लोन विधि है।

y = _.clone(x);

या आप इसे बढ़ा सकते हैं

copiedObject = _.extend({},originalObject);

2
धन्यवाद। उल्का सर्वर पर इस तकनीक का उपयोग करना।
टर्बो

जल्द से जल्द शुरू करने के लिए, मैं एनपीएम सीखने की सलाह दूंगा, Browserify, साथ ही साथ दर्ज करें मुझे 'npm i --save lodash.clone' के साथ काम करने के लिए क्लोन मिला और फिर 'var क्लोन = आवश्यकता (' lodash.clone ');' काम करने के लिए आपको ब्राउज़र की आवश्यकता होती है। एक बार जब आप इसे स्थापित कर लेते हैं और सीखते हैं कि यह कैसे काम करता है, तो आप हर बार जब आप अपना कोड चलाते हैं (क्रोम सीधे तौर पर क्रोम में जाने के बजाय) तो 'indexfile.js> bundle.js' शुरू करें। यह आपकी फ़ाइल और npm मॉड्यूल से बंडल में आवश्यक सभी फाइलों को इकट्ठा करता है। आप शायद समय बचा सकते हैं और इस कदम को स्वचालित रूप से गुलप के साथ जोड़ सकते हैं।
हारून बेल

64

ठीक है, कल्पना कीजिए कि आपके पास यह वस्तु है और आप इसे क्लोन करना चाहते हैं:

let obj = {a:1, b:2, c:3}; //ES6

या

var obj = {a:1, b:2, c:3}; //ES5

जवाब में मुख्य रूप से depeneds जिस पर है ECMAScript आप उपयोग कर, में ES6+, आप बस का उपयोग कर सकते Object.assignक्लोन करने के लिए:

let cloned = Object.assign({}, obj); //new {a:1, b:2, c:3};

या इस तरह प्रसार ऑपरेटर का उपयोग कर:

let cloned = {...obj}; //new {a:1, b:2, c:3};

लेकिन अगर आप उपयोग कर रहे हैं ES5, तो आप कुछ तरीकों का उपयोग कर सकते हैं, लेकिन JSON.stringify, बस यह सुनिश्चित करें कि आप डेटा का एक बड़ा हिस्सा कॉपी करने के लिए उपयोग नहीं कर रहे हैं, लेकिन यह कई मामलों में एक ही तरह से आसान तरीका हो सकता है:

let cloned = JSON.parse(JSON.stringify(obj)); 
//new {a:1, b:2, c:3};, can be handy, but avoid using on big chunk of data over and over

क्या आप इस बात का उदाहरण दे सकते हैं कि क्या big chunk of dataसमानता होगी? 100kb? 100 एमबी? धन्यवाद!
user1063287

हां, @ user1063287, कि मूल रूप से बड़ा डेटा, खराब प्रदर्शन ... तो यह वास्तव में निर्भर करता है, एक केबी, एमबी या जीबी नहीं, यह कितनी बार आप इसे भी करना चाहते हैं के बारे में अधिक है ... यह भी काम नहीं करेगा कार्यों और अन्य सामानों के लिए ...
एलेरिज़ा

3
Object.assignएक उथली प्रतिलिपि बनाता है (बस प्रसार के रूप में, @ एलीज़ेरा)
बोगदान डी

आप es5: ^) @Alireza
SensationSama

40

एक विशेष रूप से अभिमानी समाधान उन वस्तुओं की गहरी प्रतियां बनाने के लिए JSON एन्कोडिंग का उपयोग करना है जिनके पास सदस्य विधियां नहीं हैं। JSON के लिए कार्यप्रणाली आपके लक्ष्य ऑब्जेक्ट को एनकोड करती है, फिर इसे डिकोड करके, आपको वह कॉपी मिलती है जिसे आप खोज रहे हैं। आप जितनी बार चाहें उतनी बार डिकोड कर सकते हैं।

बेशक, फ़ंक्शन JSON में नहीं हैं, इसलिए यह केवल सदस्य विधियों के बिना ऑब्जेक्ट के लिए काम करता है।

यह कार्यप्रणाली मेरे उपयोग के मामले के लिए एकदम सही थी, क्योंकि मैं JSON ब्लॉब्स को एक कुंजी-मूल्य की दुकान में संग्रहीत कर रहा हूं, और जब वे एक जावास्क्रिप्ट एपीआई में वस्तुओं के रूप में उजागर होते हैं, तो प्रत्येक वस्तु में वास्तव में ऑब्जेक्ट की मूल स्थिति की एक प्रति शामिल होती है इसलिए हम कॉलकर्ता द्वारा उजागर वस्तु को म्यूट करने के बाद डेल्टा की गणना कर सकते हैं।

var object1 = {key:"value"};
var object2 = object1;

object2 = JSON.stringify(object1);
object2 = JSON.parse(object2);

object2.key = "a change";
console.log(object1);// returns value

कार्य JSON से संबंधित क्यों नहीं हैं? मैंने उन्हें JSON के रूप में एक बार फिर स्थानांतरित किया है ...
The_drow

5
कार्य JSON युक्ति का हिस्सा नहीं हैं क्योंकि वे डेटा को स्थानांतरित करने के लिए एक सुरक्षित (या स्मार्ट) तरीका नहीं हैं, जो कि JSON के लिए बनाया गया था। मैं जानता हूँ कि फ़ायरफ़ॉक्स में देशी JSON एनकोडर बस इसे करने के लिए पारित कार्यों की अनदेखी करता है, लेकिन मैं दूसरों के व्यवहार के बारे में निश्चित नहीं हूं।
क्रिस वाकर

1
@ चिह्न: { 'foo': function() { return 1; } }एक शाब्दिक-निर्मित वस्तु है।
1

@abarnert फ़ंक्शन डेटा नहीं हैं। "फंक्शन शाब्दिक" एक मिथ्या नाम है - चूंकि कार्यों में मनमाने ढंग से कोड शामिल हो सकते हैं, जिसमें असाइनमेंट और सभी प्रकार के "गैर-अनुक्रमिक" चीजें शामिल हैं।
vemv

35

आप संदर्भ के बिना किसी ऑब्जेक्ट को कॉपी करने के लिए बस एक फैली हुई संपत्ति का उपयोग कर सकते हैं । लेकिन सावधान रहें (टिप्पणियां देखें), 'कॉपी' केवल सबसे निचले ऑब्जेक्ट / सरणी स्तर पर है। नेस्टेड गुण अभी भी संदर्भ हैं!


पूरा क्लोन:

let x = {a: 'value1'}
let x2 = {...x}

// => mutate without references:

x2.a = 'value2'
console.log(x.a)    // => 'value1'

दूसरे स्तर पर संदर्भ के साथ क्लोन:

const y = {a: {b: 'value3'}}
const y2 = {...y}

// => nested object is still a references:

y2.a.b = 'value4'
console.log(y.a.b)    // => 'value4'

जावास्क्रिप्ट वास्तव में मूल रूप से गहरे क्लोनों का समर्थन नहीं करता है। एक उपयोगिता समारोह का उपयोग करें। उदाहरण के लिए रामदा:

http://ramdajs.com/docs/#clone


1
यह काम नहीं कर रहा है ... यह शायद तब काम करेगा जब x उदाहरण के लिए एक सरणी होगा x = ['ab', 'cd', ...]
Kamil Kiełczewski

3
यह काम करता है, लेकिन यह ध्यान में रखना है कि यह एक नकली प्रतिलिपि है, इसलिए किसी भी अन्य वस्तुओं के गहरे संदर्भ संदर्भ बने रहते हैं!
कीड़े बनी

एक आंशिक क्लोन इस तरह से भी हो सकता है:const first = {a: 'foo', b: 'bar'}; const second = {...{a} = first}
क्रिस्चियन Tra 13na

25

AngularJS का उपयोग करने वालों के लिए, इस पुस्तकालय में वस्तुओं के क्लोनिंग या विस्तार के लिए प्रत्यक्ष विधि भी है।

var destination = angular.copy(source);

या

angular.copy(source, destination);

Angular.copy प्रलेखन में अधिक ...


2
यह एक गहरी प्रतिलिपि है FYI करें।
zamnuts

22

यहां एक फ़ंक्शन है जिसका आप उपयोग कर सकते हैं।

function clone(obj) {
    if(obj == null || typeof(obj) != 'object')
        return obj;    
    var temp = new obj.constructor(); 
    for(var key in obj)
        temp[key] = clone(obj[key]);    
    return temp;
}

10
यह उत्तर बहुत करीब है, लेकिन काफी सही नहीं है। यदि आप किसी दिनांक ऑब्जेक्ट को क्लोन करने का प्रयास करते हैं, तो आपको एक ही तिथि नहीं मिलेगी क्योंकि दिनांक निर्माण फ़ंक्शन को कॉल नई दिनांक को वर्तमान दिनांक / समय के साथ प्रारंभ करता है। यह मान पर्याप्त नहीं है और इसके लिए / लूप में कॉपी नहीं किया जाएगा।
ए। लेवी

सही नहीं है, लेकिन उन बुनियादी मामलों के लिए अच्छा है। उदाहरण के लिए एक तर्क के साधारण क्लोनिंग की अनुमति देना जो एक मूल वस्तु, सरणी या स्ट्रिंग हो सकता है।
james_womack

का उपयोग कर सही ढंग से निर्माण करने वाले को बुलाने के लिए बनाया गया new। स्वीकृत उत्तर नहीं है।
GetFree

नोड पर काम करता है बाकी सब! अभी भी बायाँ संदर्भ लिंक
user956584

पुनरावर्ती विचार महान है। लेकिन यदि मान सरणी है, तो यह काम करेगा?
Q10Viking

22

ए। लेवी का जवाब लगभग पूरा हो गया है, यहां मेरा थोड़ा योगदान है: एक तरीका है कि कैसे पुनरावर्ती संदर्भों को संभालना है , इस पंक्ति को देखें

if(this[attr]==this) copy[attr] = copy;

यदि ऑब्जेक्ट XML DOM एलिमेंट है, तो हमें इसके बजाय cloneNode का उपयोग करना चाहिए

if(this.cloneNode) return this.cloneNode(true);

A.Levy के संपूर्ण अध्ययन और केल्विन के प्रोटोटाइप दृष्टिकोण से प्रेरित होकर, मैं इस समाधान की पेशकश करता हूं:

Object.prototype.clone = function() {
  if(this.cloneNode) return this.cloneNode(true);
  var copy = this instanceof Array ? [] : {};
  for(var attr in this) {
    if(typeof this[attr] == "function" || this[attr]==null || !this[attr].clone)
      copy[attr] = this[attr];
    else if(this[attr]==this) copy[attr] = copy;
    else copy[attr] = this[attr].clone();
  }
  return copy;
}

Date.prototype.clone = function() {
  var copy = new Date();
  copy.setTime(this.getTime());
  return copy;
}

Number.prototype.clone = 
Boolean.prototype.clone =
String.prototype.clone = function() {
  return this;
}

जवाब में एंडी बर्क का नोट भी देखें।


3
Date.prototype.clone = function() {return new Date(+this)};
रॉब डे

22

इस लेख से: ब्रायन Huisman द्वारा जावास्क्रिप्ट में सरणियों और वस्तुओं की प्रतिलिपि कैसे करें :

Object.prototype.clone = function() {
  var newObj = (this instanceof Array) ? [] : {};
  for (var i in this) {
    if (i == 'clone') continue;
    if (this[i] && typeof this[i] == "object") {
      newObj[i] = this[i].clone();
    } else newObj[i] = this[i]
  } return newObj;
};

4
यह करीब है, लेकिन किसी भी वस्तु के लिए काम नहीं करता है। इसके साथ दिनांक ऑब्जेक्ट क्लोनिंग का प्रयास करें। सभी संपत्तियां असंख्य नहीं हैं, इसलिए वे सभी लूप के लिए / में दिखाई नहीं देंगे।
ए। लेवी

इस तरह वस्तु प्रोटोटाइप में जोड़ना मेरे लिए jQuery टूट गया। यहां तक ​​कि जब मैंने क्लोन 2 का नाम बदला।
iPadDeveloper2011

3
@ iPadDeveloper2011 ऊपर दिए गए कोड में एक बग था जहां इसने 'i' (इसके लिए मैं) 'नामक एक वैश्विक वैरिएबल बनाया था, बजाय' (इस में var i के लिए) '। मेरे पास इसे संपादित करने और इसे ठीक करने के लिए पर्याप्त कर्म हैं।
मिकमेकाना

1
@ कलालीन: इसे एक गैर-संपत्ति के रूप में बनाया जाना चाहिए, अन्यथा 'लूप' के लिए 'क्लोन' दिखाई देगा।
मिकमेकाना

2
क्यों var copiedObj = Object.create(obj);एक महान तरीका नहीं है?
दान पी।

19

ES-6 में आप बस Object.assign (...) का उपयोग कर सकते हैं। उदाहरण के लिए:

let obj = {person: 'Thor Odinson'};
let clone = Object.assign({}, obj);

एक अच्छा संदर्भ यहाँ है: https://googlechrome.github.io/samples/object-assign-es6/


12
यह वस्तु का गहरा क्लोन नहीं करता है।
अगस्त

यह एक काम है, एक प्रति नहीं है। clone.Title = "बस एक क्लोन" का अर्थ है कि obj.Title = "सिर्फ एक क्लोन"।
होल्डऑफहंगर

@HoldOffHunger आप गलत हैं। इसे अपने ब्राउज़र के जेएस कंसोल ( let obj = {person: 'Thor Odinson'}; let clone = Object.assign({}, obj); clone.title = "Whazzup";)
कोलैप्सर

@ कोल्पोलर: यह ठीक वही है जो मैंने जांचा था, फिर कंसोल.लॉग (व्यक्ति) "व्हाज़अप" होगा, न कि "थॉर ओडिनसन"। अगस्त की टिप्पणी देखें
होल्डऑफहंगर

1
@HoldOffHunger Chrome 60.0.3112.113 में और न ही एज 14.14393 में होता है; अगस्त की टिप्पणी लागू नहीं होती है क्योंकि आदिम प्रकार के objगुणों के मूल्यों को वास्तव में क्लोन किया जाता है। संपत्ति मूल्य जो स्वयं वस्तुएं हैं, उन्हें क्लोन नहीं किया जाएगा।
कॉलप्पर

18

ECMAScript 2018 में

let objClone = { ...obj };

ज्ञात हो कि नेस्टेड वस्तुओं को अभी भी एक संदर्भ के रूप में कॉपी किया जाता है।


1
संकेत के लिए धन्यवाद कि नेस्टेड वस्तुओं को अभी भी एक संदर्भ के रूप में कॉपी किया जाता है! अपने कोड को डीबग करते समय मैं लगभग पागल हो गया क्योंकि मैंने "क्लोन" पर नेस्टेड गुणों को संशोधित किया लेकिन मूल संशोधित हो गया।
बेनी न्युगबॉएर

यह ईएस २०१६ है, २०१, नहीं, और यह जवाब दो साल पहले दिया गया था
दानस्केलस्कु

तो क्या हुआ अगर मैं नेस्टेड संपत्ति की प्रतिलिपि भी चाहता हूं
सुनील गर्ग

1
@SunilGarg नेस्टेड प्रॉपर्टी को कॉपी करने के लिए आपconst objDeepClone = JSON.parse(JSON.stringify(obj));
Pavan Garre

16

लोडश का उपयोग करना:

var y = _.clone(x, true);

5
OMG यह क्लोनिंग को रोकने के लिए पागल होगा। यह एकमात्र समझदार जवाब है।
डेन रॉस

5
मैं पसंद _.cloneDeep(x)करता हूं क्योंकि यह अनिवार्य रूप से ऊपर की तरह ही है, लेकिन बेहतर पढ़ता है।
गरबनज़ियो

14

साधारण वस्तुओं की क्लोनिंग में रुचि:

JSON.parse(JSON.stringify(json_original));

स्रोत: संदर्भ द्वारा जावास्क्रिप्ट ऑब्जेक्ट को नए चर में कॉपी कैसे करें?


बहुत अच्छा - सरल।
मैट एच

@ मत्ता: यह जवाब पहले से ही 2012 में दिया गया था । आपने इसे देखा था? मोहम्मद, क्या आपने उनमें से किसी एक की नकल करने से पहले मौजूदा उत्तरों की जांच की?
दानस्केलस्कु

एक तरह से अच्छा है। ty ने ऐसा कभी नहीं सोचा था
1-14x0r

13

आप किसी ऑब्जेक्ट को क्लोन कर सकते हैं और कोड की एक लाइन का उपयोग करके पिछले एक से किसी भी संदर्भ को हटा सकते हैं। बस करो:

var obj1 = { text: 'moo1' };
var obj2 = Object.create(obj1); // Creates a new clone without references

obj2.text = 'moo2'; // Only updates obj2's text property

console.log(obj1, obj2); // Outputs: obj1: {text:'moo1'}, obj2: {text:'moo2'}

ब्राउज़र / इंजन के लिए जो वर्तमान में Object.create का समर्थन नहीं करते हैं आप इस पॉलीफ़िल का उपयोग कर सकते हैं:

// Polyfill Object.create if it does not exist
if (!Object.create) {
    Object.create = function (o) {
        var F = function () {};
        F.prototype = o;
        return new F();
    };
}

1
+1 Object.create(...)निश्चित रूप से जाने का रास्ता है।
रेने न्यफेनेगर

एकदम सही जवाब। शायद आप के लिए एक स्पष्टीकरण जोड़ सकते हैं Object.hasOwnProperty? इस तरह लोगों को पता है कि प्रोटोटाइप लिंक को खोजने से कैसे रोका जाए।
at४ में फ्रेजिनवासियन

अच्छी तरह से काम करता है लेकिन पॉलीफिल किस ब्राउज़र में काम करता है?
इयान लुन

11
यह एक obj1 के साथ obj2 बना रहा है क्योंकि यह प्रोटोटाइप है। यह केवल इसलिए काम करता है क्योंकि आप textसदस्य को obj2 में छाया दे रहे हैं । आप एक प्रति नहीं बना रहे हैं, जब किसी सदस्य को obj2 पर नहीं मिला है, तो केवल प्रोटोटाइप श्रृंखला को संदर्भित करता है।
निक डेसुलेनीर्स

2
यह इसे "संदर्भों के बिना" नहीं बनाता है, यह सिर्फ प्रोटोटाइप के संदर्भ को स्थानांतरित करता है। यह अभी भी एक संदर्भ है। यदि कोई संपत्ति मूल में बदलती है तो "क्लोन" में प्रोटोटाइप संपत्ति होगी। यह एक क्लोन नहीं है।
जिम्बो जॉनी

13

एक पुराने प्रश्न का नया उत्तर! यदि आपके पास स्प्रेड सिंटैक्स के साथ ECMAScript 2016 (ES6) का उपयोग करने की खुशी है , तो यह आसान है।

keepMeTheSame = {first: "Me!", second: "You!"};
cloned = {...keepMeTheSame}

यह किसी वस्तु की उथली प्रतिलिपि के लिए एक स्वच्छ विधि प्रदान करता है। एक गहरी प्रतिलिपि बनाना, जिसका अर्थ है प्रत्येक पुनरावर्ती नेस्टेड ऑब्जेक्ट में हर मूल्य की एक नई प्रतिलिपि तैयार करना, ऊपर के भारी समाधानों की आवश्यकता होती है।

जावास्क्रिप्ट विकसित होता रहता है।


2
जब आप वस्तुओं पर परिभाषित कार्य करते हैं तो यह काम नहीं करता है
पेट्र मारेक

जहाँ तक मुझे लगता है कि प्रसार ऑपरेटर केवल iterables के साथ काम करता है - developer.mozilla.org कहता है:var obj = {'key1': 'value1'}; var array = [...obj]; // TypeError: obj is not iterable
Oleh

@ ओले का उपयोग करें [... obj] के बजाय `{... obj};`
manikant gautam

@manikantgautam I पहले Object.assign () का उपयोग कर रहा था, लेकिन अब वास्तव में ऑब्जेक्ट फैली हुई सिंटैक्स नवीनतम क्रोम, फ़ायरफ़ॉक्स (अभी भी एज और सफारी में नहीं) में समर्थित है। इसका ECMAScript प्रस्ताव ... लेकिन बाबेल इसका समर्थन करता है जहां तक ​​मैं देख सकता हूं, इसलिए शायद इसका उपयोग करना सुरक्षित है।
ओलेह

12
let clone = Object.assign( Object.create( Object.getPrototypeOf(obj)), obj)

ES6 समाधान यदि आप (उथले) एक वर्ग उदाहरण क्लोन करना चाहते हैं न कि केवल एक संपत्ति वस्तु।


यह कैसे अलग है let cloned = Object.assign({}, obj)?
21:19

10

मुझे लगता है कि एक सरल और काम करने वाला उत्तर है। गहरी नकल में दो चिंताएँ हैं:

  1. गुणों को एक दूसरे से स्वतंत्र रखें।
  2. और क्लोन किए गए ऑब्जेक्ट पर तरीकों को जीवित रखें।

इसलिए मुझे लगता है कि एक सरल समाधान पहले क्रमबद्ध और deserialize होगा और फिर उस पर एक कार्य करना होगा ताकि कार्यों को भी कॉपी किया जा सके।

let deepCloned = JSON.parse(JSON.stringify(source));
let merged = Object.assign({}, source);
Object.assign(merged, deepCloned);

हालाँकि इस सवाल के कई जवाब हैं, मुझे उम्मीद है कि यह भी मदद करेगा।


हालाँकि अगर मुझे लॉश को आयात करने की अनुमति है, तो मैं लॉश का उपयोग करना पसंद करता हूं cloneDeep
ConductedClever

2
मैं JSON.parse (JSON.stringify (स्रोत)) का उपयोग कर रहा हूं। हमेशा काम करते हैं।
मीशा

2
@ मीशा, इस तरह से आप फंक्शन्स मिस कर देंगे। Has वर्क्स ’शब्द के कई अर्थ हैं।
ConductedClever

और ध्यान रखें कि, जिस तरह से मैंने उल्लेख किया है, केवल पहली परत के कार्यों को कॉपी किया जाएगा। इसलिए यदि हमारे पास एक-दूसरे के अंदर कुछ ऑब्जेक्ट हैं, तो एकमात्र तरीका फ़ील्ड को पुनरावर्ती रूप से फ़ील्ड की प्रतिलिपि बनाना है।
ConductedClever

9

एक गहरी प्रति और क्लोन के लिए, JSON.stringify तब JSON.parse ऑब्जेक्ट:

obj = { a: 0 , b: { c: 0}};
let deepClone = JSON.parse(JSON.stringify(obj));
obj.a = 5;
obj.b.c = 5;
console.log(JSON.stringify(deepClone)); // { a: 0, b: { c: 0}}

बहुत चालाक ... इस दृष्टिकोण के लिए किसी भी downsides?
Aleks

8

यह ए। लेवी के कोड का एक रूपांतर है जो फ़ंक्शन और कई / चक्रीय संदर्भों के क्लोनिंग को भी संभालता है - इसका मतलब यह है कि यदि पेड़ में दो गुण जो क्लोन किए गए हैं, वे एक ही वस्तु के संदर्भ हैं, क्लोन किए गए ऑब्जेक्ट ट्री में ये होंगे गुण संदर्भित ऑब्जेक्ट के एक और एक ही क्लोन को इंगित करते हैं। यह चक्रीय निर्भरता के मामले को भी हल करता है, जिसे अगर छोड़ दिया जाता है, तो अनंत लूप हो जाता है। एल्गोरिथ्म की जटिलता हे (n)

function clone(obj){
    var clonedObjectsArray = [];
    var originalObjectsArray = []; //used to remove the unique ids when finished
    var next_objid = 0;

    function objectId(obj) {
        if (obj == null) return null;
        if (obj.__obj_id == undefined){
            obj.__obj_id = next_objid++;
            originalObjectsArray[obj.__obj_id] = obj;
        }
        return obj.__obj_id;
    }

    function cloneRecursive(obj) {
        if (null == obj || typeof obj == "string" || typeof obj == "number" || typeof obj == "boolean") return obj;

        // Handle Date
        if (obj instanceof Date) {
            var copy = new Date();
            copy.setTime(obj.getTime());
            return copy;
        }

        // Handle Array
        if (obj instanceof Array) {
            var copy = [];
            for (var i = 0; i < obj.length; ++i) {
                copy[i] = cloneRecursive(obj[i]);
            }
            return copy;
        }

        // Handle Object
        if (obj instanceof Object) {
            if (clonedObjectsArray[objectId(obj)] != undefined)
                return clonedObjectsArray[objectId(obj)];

            var copy;
            if (obj instanceof Function)//Handle Function
                copy = function(){return obj.apply(this, arguments);};
            else
                copy = {};

            clonedObjectsArray[objectId(obj)] = copy;

            for (var attr in obj)
                if (attr != "__obj_id" && obj.hasOwnProperty(attr))
                    copy[attr] = cloneRecursive(obj[attr]);                 

            return copy;
        }       


        throw new Error("Unable to copy obj! Its type isn't supported.");
    }
    var cloneObj = cloneRecursive(obj);



    //remove the unique ids
    for (var i = 0; i < originalObjectsArray.length; i++)
    {
        delete originalObjectsArray[i].__obj_id;
    };

    return cloneObj;
}

कुछ त्वरित परीक्षण

var auxobj = {
    prop1 : "prop1 aux val", 
    prop2 : ["prop2 item1", "prop2 item2"]
    };

var obj = new Object();
obj.prop1 = "prop1_value";
obj.prop2 = [auxobj, auxobj, "some extra val", undefined];
obj.nr = 3465;
obj.bool = true;

obj.f1 = function (){
    this.prop1 = "prop1 val changed by f1";
};

objclone = clone(obj);

//some tests i've made
console.log("test number, boolean and string cloning: " + (objclone.prop1 == obj.prop1 && objclone.nr == obj.nr && objclone.bool == obj.bool));

objclone.f1();
console.log("test function cloning 1: " + (objclone.prop1 == 'prop1 val changed by f1'));
objclone.f1.prop = 'some prop';
console.log("test function cloning 2: " + (obj.f1.prop == undefined));

objclone.prop2[0].prop1 = "prop1 aux val NEW";
console.log("test multiple references cloning 1: " + (objclone.prop2[1].prop1 == objclone.prop2[0].prop1));
console.log("test multiple references cloning 2: " + (objclone.prop2[1].prop1 != obj.prop2[0].prop1));

1
सितंबर 2016 तक, यह प्रश्न का एकमात्र सही समाधान है।
डोमक्यू २

6

मैं बस सभी को जोड़ना चाहता था Object.create इस पोस्ट में समाधान , कि यह नोडज के साथ वांछित तरीके से काम नहीं करता है।

के परिणाम में फ़ायरफ़ॉक्स

var a = {"test":"test"};
var b = Object.create(a);
console.log(b);´

है

{test:"test"}

नोडज में यह है

{}

यह प्रोटोटाइप इनहेरिटेंस है, क्लोनिंग नहीं।
d13

1
@ d13 जब आपका तर्क मान्य है, तो ध्यान दें कि किसी ऑब्जेक्ट को क्लोन करने के लिए जावास्क्रिप्ट में कोई मानकीकृत तरीका नहीं है। यह प्रोटोटाइपिक इनहेरिटेंस है, लेकिन इसे क्लोन के रूप में इस्तेमाल किया जा सकता है, हालांकि यदि आप अवधारणाओं को समझते हैं।
मेंढ़क

@froginvasion। Object.create का उपयोग करने के साथ एकमात्र समस्या यह है कि नेस्टेड ऑब्जेक्ट और एरेज़ केवल प्रोटोटाइप के नेस्टेड ऑब्जेक्ट और एरेज़ के लिए पॉइंटर संदर्भ हैं। jsbin.com/EKivInO/2/edit?js,console । तकनीकी रूप से एक "क्लोन" ऑब्जेक्ट का अपना अनूठा गुण होना चाहिए जो अन्य वस्तुओं पर गुणों के संदर्भ में साझा नहीं किया जाता है।
d13

@ d13 ठीक है, मैं अब आपकी बात देख रहा हूं। लेकिन मेरा मतलब यह है कि बहुत से लोग प्रोटोटाइपिक विरासत की अवधारणा से अलग हो गए हैं, और मेरे लिए यह सीखने में विफल हैं कि यह कैसे काम करता है। अगर मैं गलत नहीं हूं, तो आपका उदाहरण केवल Object.hasOwnPropertyयह जांचने के लिए कॉल करके तय किया जा सकता है कि आप सरणी के मालिक हैं या नहीं। हाँ इससे प्रोटोटाइपीय विरासत से निपटने के लिए अतिरिक्त जटिलता बढ़ जाती है।
फ्रिगिनवासियन

6
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;
};

2
if(!src && typeof src != "object"){। मुझे लगता है कि ||नहीं होना चाहिए &&
मिकम

6

चूँकि माइंडएवर ने कहा था कि क्लोन की जाने वाली वस्तु एक 'शाब्दिक-निर्मित' वस्तु है, इसलिए इसका एक हल यह हो सकता है कि वह वस्तु का एक उदाहरण क्लोन करने के बजाय केवल कई बार ऑब्जेक्ट को उत्पन्न करे :

function createMyObject()
{
    var myObject =
    {
        ...
    };
    return myObject;
}

var myObjectInstance1 = createMyObject();
var myObjectInstance2 = createMyObject();

6

मैंने अपना स्वयं का कार्यान्वयन लिखा है। सुनिश्चित नहीं है कि यह एक बेहतर समाधान के रूप में गिना जाता है:

/*
    a function for deep cloning objects that contains other nested objects and circular structures.
    objects are stored in a 3D array, according to their length (number of properties) and their depth in the original object.
                                    index (z)
                                         |
                                         |
                                         |
                                         |
                                         |
                                         |                      depth (x)
                                         |_ _ _ _ _ _ _ _ _ _ _ _
                                        /_/_/_/_/_/_/_/_/_/
                                       /_/_/_/_/_/_/_/_/_/
                                      /_/_/_/_/_/_/...../
                                     /................./
                                    /.....            /
                                   /                 /
                                  /------------------
            object length (y)    /
*/

निम्नलिखित कार्यान्वयन है:

function deepClone(obj) {
    var depth = -1;
    var arr = [];
    return clone(obj, arr, depth);
}

/**
 *
 * @param obj source object
 * @param arr 3D array to store the references to objects
 * @param depth depth of the current object relative to the passed 'obj'
 * @returns {*}
 */
function clone(obj, arr, depth){
    if (typeof obj !== "object") {
        return obj;
    }

    var length = Object.keys(obj).length; // native method to get the number of properties in 'obj'

    var result = Object.create(Object.getPrototypeOf(obj)); // inherit the prototype of the original object
    if(result instanceof Array){
        result.length = length;
    }

    depth++; // depth is increased because we entered an object here

    arr[depth] = []; // this is the x-axis, each index here is the depth
    arr[depth][length] = []; // this is the y-axis, each index is the length of the object (aka number of props)
    // start the depth at current and go down, cyclic structures won't form on depths more than the current one
    for(var x = depth; x >= 0; x--){
        // loop only if the array at this depth and length already have elements
        if(arr[x][length]){
            for(var index = 0; index < arr[x][length].length; index++){
                if(obj === arr[x][length][index]){
                    return obj;
                }
            }
        }
    }

    arr[depth][length].push(obj); // store the object in the array at the current depth and length
    for (var prop in obj) {
        if (obj.hasOwnProperty(prop)) result[prop] = clone(obj[prop], arr, depth);
    }

    return result;
}

मेरी वस्तु के लिए काम नहीं कर रहा है, हालांकि मेरा मामला थोड़ा जटिल है।
सजुकु
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.