javascript - जावास्क्रिप्ट में एक वस्तु गहरी क्लोन करने का सबसे प्रभावी तरीका क्या है?




object clone (20)

नोट: यह किसी अन्य उत्तर का उत्तर है, इस प्रश्न का उचित जवाब नहीं। यदि आप तेजी से ऑब्जेक्ट क्लोनिंग करना चाहते हैं तो कृपया इस प्रश्न के उत्तर में कॉर्बान की सलाह का पालन ​​करें।

मैं ध्यान रखना चाहता हूं कि jQuery में .clone() विधि केवल क्लोन डीओएम तत्वों को क्लोन करता है। जावास्क्रिप्ट ऑब्जेक्ट्स क्लोन करने के लिए, आप करेंगे:

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

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

अधिक जानकारी jQuery दस्तावेज़ में पाया जा सकता है।

मैं यह भी ध्यान रखना चाहता हूं कि गहरी प्रति वास्तव में ऊपर दिखाए गए कार्यों की तुलना में बहुत अधिक स्मार्ट है - यह कई जाल से बचने में सक्षम है (उदाहरण के लिए, डीओएम तत्व को गहराई से बढ़ाने की कोशिश कर रहा है)। यह अक्सर jQuery कोर और प्लगइन में बहुत प्रभाव के लिए प्रयोग किया जाता है।

https://code.i-harness.com

जावास्क्रिप्ट ऑब्जेक्ट क्लोन करने का सबसे प्रभावी तरीका क्या है? मैंने obj = eval(uneval(o)); इस्तेमाल किया जा रहा है, लेकिन यह गैर मानक है और केवल फ़ायरफ़ॉक्स द्वारा समर्थित है

मैंने obj = JSON.parse(JSON.stringify(o)); जैसी चीजें की हैं obj = JSON.parse(JSON.stringify(o)); लेकिन दक्षता पर सवाल उठाओ।

मैंने विभिन्न त्रुटियों के साथ रिकर्सिव कॉपीिंग फ़ंक्शंस भी देखा है।
मुझे आश्चर्य है कि कोई कैनोलिक समाधान मौजूद नहीं है।


कोड की एक पंक्ति में एक ऑब्जेक्ट क्लोन (गहरे क्लोन नहीं) को क्लोन करने का प्रभावी तरीका

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

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

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

Object.assign

पुराने ब्राउज़रों का समर्थन करने के लिए पॉलीफिल :

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

AngularJS

ठीक है अगर आप कोणीय का उपयोग कर रहे हैं तो आप भी ऐसा कर सकते हैं

var newObject = angular.copy(oldObject);

इस बेंचमार्क को चेकआउट करें: http://jsben.ch/#/bWfk9

मेरे पिछले परीक्षणों में जहां गति एक मुख्य चिंता थी

JSON.parse(JSON.stringify(obj))

गहरी क्लोन को ऑब्जेक्ट का सबसे तेज़ तरीका बनने के लिए (यह 10-20% तक गहरा ध्वज सेट के साथ jQuery.extend को बाहर करता है)।

jQuery.extend बहुत तेज़ है जब गहरा ध्वज झूठा (उथला क्लोन) पर सेट होता है। यह एक अच्छा विकल्प है, क्योंकि इसमें टाइप प्रमाणीकरण के लिए कुछ अतिरिक्त तर्क शामिल हैं और अपरिभाषित गुणों आदि पर प्रतिलिपि नहीं है, लेकिन यह आपको थोड़ा धीमा कर देगा।

यदि आप उन ऑब्जेक्ट्स की संरचना को जानते हैं जिन्हें आप क्लोन करने की कोशिश कर रहे हैं या गहरे घोंसले वाले सरणी से बच सकते हैं, तो आप हैऑनप्रॉपर्टी की जांच करते समय अपनी ऑब्जेक्ट क्लोन करने के for (var i in obj) लूप के लिए एक सरल लिख सकते हैं और यह jQuery से बहुत तेज होगा।

आखिरकार यदि आप हॉट लूप में किसी ज्ञात ऑब्जेक्ट स्ट्रक्चर को क्लोन करने का प्रयास कर रहे हैं तो आप क्लोन प्रक्रिया को आसानी से अस्तर करके और ऑब्जेक्ट मैन्युअल रूप से निर्माण करके बहुत अधिक सटीकता प्राप्त कर सकते हैं।

जावास्क्रिप्ट ट्रेस इंजन for..in loops के अनुकूलन पर चूसते हैं और जांच की हैऑनप्रोपर्टी आपको भी धीमा कर देगी। गति पूर्ण होने पर मैन्युअल क्लोन।

var clonedObject = {
  knownProp: obj.knownProp,
  ..
}

Date वस्तुओं पर JSON.parse(JSON.stringify(obj)) विधि का उपयोग करके सावधान रहें - JSON.stringify(new Date()) आईएसओ प्रारूप में दिनांक की स्ट्रिंग प्रस्तुति देता है, जो JSON.parse() वापस परिवर्तित नहीं होता है एक Date वस्तु के लिए। अधिक जानकारी के लिए यह उत्तर देखें

इसके अतिरिक्त, कृपया ध्यान दें कि, क्रोम 65 में कम से कम, मूल क्लोनिंग जाने का तरीका नहीं है। इस जेएसपीएआरएफ के अनुसार, एक नया फ़ंक्शन बनाकर देशी क्लोनिंग करना JSON.stringify का उपयोग करने से लगभग 800x धीमी है जो बोर्ड भर में अविश्वसनीय रूप से तेज़ है।


किसी ऑब्जेक्ट को Cloning करना हमेशा जेएस में चिंता का विषय था, लेकिन यह सब ईएस 6 से पहले था, मैं नीचे जावास्क्रिप्ट में ऑब्जेक्ट की प्रतिलिपि बनाने के विभिन्न तरीकों की सूची देता हूं, कल्पना करें कि आपके पास नीचे ऑब्जेक्ट है और इसकी गहरी प्रतिलिपि बनाना चाहेंगे:

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

उत्पत्ति को बदलने के बिना, इस ऑब्जेक्ट को कॉपी करने के कुछ तरीके हैं:

1) ES5 +, आपके लिए प्रतिलिपि करने के लिए एक साधारण फ़ंक्शन का उपयोग करना:

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 और JSON.stringify का उपयोग कर।

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

3) कोणीय जेएस:

var  deepCopyObj = angular.copy(obj);

4) jQuery:

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

5) अंडरस्कोर जेएस और लोडैश:

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

आशा है कि ये मदद ...


कोड:

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

परीक्षा:

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

मैं यही उपयोग कर रहा हूं:

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

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

var newObject = _.clone(oldObject);

यह मानते हुए कि आपके पास केवल वेरिएबल हैं और आपके ऑब्जेक्ट में कोई फ़ंक्शन नहीं है, आप बस इसका उपयोग कर सकते हैं:

var newObject = JSON.parse(JSON.stringify(oldObject));

जावास्क्रिप्ट में गहरी प्रतिलिपि वस्तुओं (मुझे लगता है कि सबसे अच्छा और सबसे सरल)

1. JSON.parse का उपयोग (JSON.stringify (ऑब्जेक्ट));

var obj = { 
  a: 1,
  b: { 
    c: 2
  }
}
var newObj = JSON.parse(JSON.stringify(obj));
obj.b.c = 20;
console.log(obj); // { a: 1, b: { c: 20 } }
console.log(newObj); // { a: 1, b: { c: 2 } } 

2. बनाई गई विधि का उपयोग करना

function cloneObject(obj) {
    var clone = {};
    for(var i in obj) {
        if(obj[i] != null &&  typeof(obj[i])=="object")
            clone[i] = cloneObject(obj[i]);
        else
            clone[i] = obj[i];
    }
    return clone;
}

var obj = { 
  a: 1,
  b: { 
    c: 2
  }
}
var newObj = cloneObject(obj);
obj.b.c = 20;

console.log(obj); // { a: 1, b: { c: 20 } }
console.log(newObj); // { a: 1, b: { c: 2 } } 

3. लो-डैश के _cloneDip लिंक lodash का उपयोग करनाlodash

var obj = { 
  a: 1,
  b: { 
    c: 2
  }
}

var newObj = _.cloneDeep(obj);
obj.b.c = 20;
console.log(obj); // { a: 1, b: { c: 20 } }
console.log(newObj); // { a: 1, b: { c: 2 } } 

4. Object.assign () विधि का उपयोग करना

var obj = { 
  a: 1,
  b: 2
}

var newObj = _.clone(obj);
obj.b = 20;
console.log(obj); // { a: 1, b: 20 }
console.log(newObj); // { a: 1, b: 2 }  

लेकिन जब गलत हो

var obj = { 
  a: 1,
  b: { 
    c: 2
  }
}

var newObj = Object.assign({}, obj);
obj.b.c = 20;
console.log(obj); // { a: 1, b: { c: 20 } }
console.log(newObj); // { a: 1, b: { c: 20 } } --> WRONG
// Note: Properties on the prototype chain and non-enumerable properties cannot be copied.

5. Underscore.js _.clone लिंक Underscore.js

var obj = { 
  a: 1,
  b: 2
}

var newObj = _.clone(obj);
obj.b = 20;
console.log(obj); // { a: 1, b: 20 }
console.log(newObj); // { a: 1, b: 2 }  

लेकिन जब गलत हो

var obj = { 
  a: 1,
  b: { 
    c: 2
  }
}

var newObj = _.cloneDeep(obj);
obj.b.c = 20;
console.log(obj); // { a: 1, b: { c: 20 } }
console.log(newObj); // { a: 1, b: { c: 20 } } --> WRONG
// (Create a shallow-copied clone of the provided plain object. Any nested objects or arrays will be copied by reference, not duplicated.)

संदर्भ माध्यम.com

JSBEN.CH प्रदर्शन बेंचमार्किंग प्लेग्राउंड 1 ~ 3 http://jsben.ch/KVQLd


कोंनोय के उत्तर का एक संस्करण यहां दिया गया है जो कि कन्स्ट्रक्टर के पैरामीटर की आवश्यकता होने पर भी काम करता है:

//If Object.create isn't already defined, we just do the simple shim,
//without the second argument, since that's 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;
}

यह फ़ंक्शन मेरी simpleoo पुस्तकालय में भी उपलब्ध है ।

संपादित करें:

यहां एक और मजबूत संस्करण है (जस्टिन मैककंडलेस के लिए धन्यवाद, यह अब चक्रीय संदर्भों का भी समर्थन करता है):

/**
 * Deep copy an object (make copies of all its object properties, sub-properties, etc.)
 * An improved version of http://keithdevens.com/weblog/archive/2007/Jun/07/javascript.clone
 * that doesn't break if the constructor has required parameters
 * 
 * It also borrows some code from http://.com/a/11621004/560114
 */ 
function deepCopy(src, /* INTERNAL */ _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's 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 object(o) {
    function F() {}
    F.prototype = o;
    return new F();
}

var newObject = object(oldObject);

यह terse है, उम्मीद के रूप में काम करता है और आपको पुस्तकालय की आवश्यकता नहीं है।

संपादित करें:

यह एक पॉलीफिल है Object.create, इसलिए आप इसका भी उपयोग कर सकते हैं।

var newObject = Object.create(oldObject);

नोट: यदि आप इनमें से कुछ का उपयोग करते हैं, तो आपको कुछ पुनरावृत्ति के साथ समस्या हो सकती है जो उपयोग करते हैं hasOwnProperty। क्योंकि, createविरासत में आने वाली नई खाली वस्तु बनाएं oldObject। लेकिन यह क्लोनिंग ऑब्जेक्ट्स के लिए अभी भी उपयोगी और व्यावहारिक है।

उदाहरण के लिए अगर oldObject.a = 5;

newObject.a; // is 5

परंतु:

oldObject.hasOwnProperty(a); // is true
newObject.hasOwnProperty(a); // is false

जो लोग JSON.parse(JSON.stringify(obj))संस्करण का उपयोग करना चाहते हैं , उनके लिए डेट ऑब्जेक्ट्स खोए बिना, आप तारों को वापस दिनांक में बदलने के लिए विधि के दूसरे तर्क काparse उपयोग कर सकते हैं :

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

निम्नलिखित एक ही वस्तु के दो उदाहरण बनाता है। मैंने इसे पाया और वर्तमान में इसका उपयोग कर रहा हूं। यह उपयोग करना आसान और आसान है।

var objToCreate = JSON.parse(JSON.stringify(cloneThis));

यह आम तौर पर सबसे कुशल समाधान नहीं है, लेकिन यह वही करता है जो मुझे चाहिए। नीचे सरल परीक्षण मामले ...

function clone(obj, clones) {
    // Makes a deep copy of 'obj'. Handles cyclic structures by
    // tracking cloned obj's 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
f.a = a
ff = clone(f)
ff === f //=> true
ff.a === a //=> false

यहां एक व्यापक क्लोन () विधि है जो किसी भी जावास्क्रिप्ट ऑब्जेक्ट को क्लोन कर सकती है। यह लगभग सभी मामलों को संभालता है:

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

शैल कॉपी एक लाइनर ( ईसीएमएस्क्रिप्ट 5 वां संस्करण ):

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

और उथले प्रतिलिपि एक लाइनर ( ईसीएमएस्क्रिप्ट 6 वां संस्करण , 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

सरणी जैसी वस्तुओं के लिए अभी तक कोई आदर्श गहरा क्लोन ऑपरेटर नहीं लगता है। जैसा कि नीचे दिया गया कोड दिखाता है, जॉन रेजिग का jQuery क्लोनर गैर-संख्यात्मक गुणों वाले सरणी को उन वस्तुओं में बदल देता है जो सरणी नहीं हैं, और RegDwight के JSON क्लोनर गैर-संख्यात्मक गुणों को छोड़ देता है। निम्नलिखित परीक्षण कई बिंदुओं पर इन बिंदुओं को चित्रित करते हैं:

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)

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

function clone(obj)
 { var clone = {};
   clone.prototype = obj.prototype;
   for (property in obj) clone[property] = obj[property];
   return clone;
 }




clone