javascript - example - object assign




كيف يمكنني استنساخ كائن JavaScript بشكل صحيح؟ (20)

لدي كائن ، x . أود أن نسخها ككائن y ، مثل أن التغييرات على y لا تعدل x . أدركت أن نسخ الكائنات المستمدة من كائنات جافا سكريبت المضمنة سيؤدي إلى خصائص إضافية غير مرغوب فيها. هذه ليست مشكلة ، لأنني نسخ واحدة من بلدي ، الأشياء التي شيدت الحرفي.

كيف يمكنني استنساخ كائن JavaScript بشكل صحيح؟


طريقة أنيقة لنسخ كائن Javascript في سطر واحد من التعليمات البرمجية

Object.assign طريقة 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;
    }
  });
}

أحد الحلول غير المتميزة بشكل خاص هو استخدام تشفير 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

إذا كنت لا تستخدم وظائف داخل الكائن الخاص بك ، يمكن أن تكون بطانة واحدة بسيطة للغاية ما يلي:

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

هذا يعمل لجميع أنواع الكائنات التي تحتوي على كائنات ، صفائف ، سلاسل ، منطقية وأرقام.

راجع أيضًا هذا المقال حول خوارزمية النسخ المتماثل المنظم للمتصفحات والتي يتم استخدامها عند نشر الرسائل من وإلى عامل ما. كما يحتوي على وظيفة للاستنساخ العميق.


الإجابة A.Levy تكاد تكون كاملة ، وهنا مساهمتي الصغيرة: هناك طريقة للتعامل مع المراجع المتكررة ، انظر هذا الخط

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

إذا كان الكائن هو عنصر DOM XML ، يجب أن نستخدم 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;
}

انظر أيضًا ملاحظة آندي بيرك في الإجابات.


في ECMAScript 6 يوجد أسلوب Object.assign ، الذي ينسخ قيم كل الخصائص الخاصة التي يمكن Object.assign من كائن إلى آخر. فمثلا:

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

ولكن انتبه إلى أن الكائنات المتداخلة لا تزال تُنسخ كمرجع.


لأولئك الذين يستخدمون AngularJS ، هناك أيضا طريقة مباشرة لاستنساخ أو تمديد الكائنات الموجودة في هذه المكتبة.

var destination = angular.copy(source);

أو

angular.copy(source, destination);

المزيد في documentation angular.copy ...


للقيام بذلك لأي كائن في JavaScript لن يكون بسيطًا أو مباشرًا. سوف تواجه مشكلة في التقاط الصفات من النموذج الأولي للكائن الذي ينبغي تركه في النموذج الأولي وعدم نسخه إلى المثيل الجديد. على سبيل المثال ، إذا كنت تضيف طريقة Object.prototype إلى Object.prototype ، كما توضح بعض الإجابات ، فستحتاج إلى تخطي تلك السمة بشكل صريح. ولكن ماذا لو كانت هناك طرق إضافية أخرى مضافة إلى Object.prototype ، أو نماذج وسيطة أخرى ، لا تعرف عنها؟ في هذه الحالة ، ستقوم بنسخ السمات التي لا يجب عليك القيام بها ، لذلك تحتاج إلى اكتشاف سمات غير متوقعة وغير محلية باستخدام طريقة hasOwnProperty .

بالإضافة إلى سمات غير قابلة للتعداد ، ستواجه مشكلة أصعب عند محاولة نسخ كائنات لها خصائص مخفية. على سبيل المثال ، يعتبر prototype خاصية مخفية لدالة ما. أيضًا ، تتم الإشارة إلى النموذج الأولي للكائن باستخدام السمة __proto__ ، التي تكون مخفية أيضًا ، ولن يتم نسخها من خلال حلقة for / in المتتالية فوق سمات الكائن المصدر. أعتقد أن __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);

ﺳﺗﮐون ﺳﻟﺳﻟﺔ اﻟﺗﺎرﯾﺦ ﻟـ d1 ﺧﻣس ﺛوان ﺑﻌد ﺳﺟل d2 . طريقة لجعل أحدهما Date هو نفسه setTime عن طريق استدعاء الأسلوب setTime ، لكن ذلك setTime لفئة Date . لا أعتقد أن هناك حلًا عامًا مضادًا لهذه المشكلة ، رغم أنني سأكون سعيدًا أن أكون مخطئًا!

عندما اضطررت إلى تنفيذ نسخ عميق عمومي ، انتهى بي الأمر إلى تسوية ، بافتراض أني سأحتاج فقط إلى نسخ Object عادي ، أو Array ، أو Date ، أو String ، أو Number ، أو Boolean . آخر 3 أنواع غير قابلة للتغيير ، لذلك يمكنني إجراء نسخة ضحلة وعدم القلق بشأن تغييرها. لقد افترضت كذلك أن أي عناصر موجودة في Object أو Array ستكون أيضًا أحد الأنواع الستة البسيطة في تلك القائمة. يمكن تحقيق ذلك برمز كالتالي:

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

ستعمل الوظيفة المذكورة أعلاه بشكل ملائم للأنواع الستة البسيطة التي ذكرتها ، طالما أن البيانات في الكائنات والمصفوفات تشكل بنية شجرية. بمعنى ، لا يوجد أكثر من مرجع واحد إلى نفس البيانات في الكائن. فمثلا:

// 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  due to infinite recursion:
var cyclicGraph = {
    "left"  : { "left" : null, "right" : null, "data" : 3 },
    "data"  : 8
};
cyclicGraph["right"] = cyclicGraph;

لن تكون قادرة على التعامل مع أي كائن جافا سكريبت ، ولكنها قد تكون كافية لكثير من الأغراض طالما أنك لا تفترض أنها ستعمل فقط على أي شيء تقوم به.


من هذه المقالة: كيفية نسخ صفائف والكائنات في Javascript بواسطة Brian 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;
};

هناك العديد من المشكلات المتعلقة بمعظم الحلول على الإنترنت. لذلك قررت أن أقوم بمتابعة ، والتي تشمل ، لماذا لا ينبغي قبول الإجابة المقبولة.

بداية الوضع

أريد أن أعمل نسخًا عميقة لجافا سكريبت مع جميع أطفالها وأطفالهم وما إلى ذلك. ولكن بما أنني لست نوعًا من مطور عادي ، فإن " Object له properties طبيعية ، circular structures ، وحتى nested objects .

لذلك دعونا إنشاء circular structure nested object أولاً.

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

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

دعونا نجلب كل شيء معًا في Object اسمه.

var a = {
    x: 'a',
    circ: new Circ(),
    nested: new Nested('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

الحل الأصلي

بعد مجادلة مع زملائي في العمل ، سألني رئيسنا عما حدث ، ووجد حلًا بسيطًا بعد بعض googling. انها تسمى Object.create .

var b = Object.create(a);

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

تمت إضافة هذا الحل إلى Javascript منذ بعض الوقت ، بل ويتعامل مع 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"
    }
}

... وترى ، أنه لم يعمل مع الهيكل المتداخل الداخل.

polyfill عن الحل الأصلي

هناك Object.create لـ Object.create في المتصفح الأقدم تمامًا مثل IE 8. إنه شيء مثل ما أوصت به Mozilla ، وبالطبع ، فهو ليس مثاليًا ويؤدي إلى نفس المشكلة مثل الحل الأصلي .

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

var b = clonePF(a);

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

لقد وضعت F خارج النطاق حتى يمكننا إلقاء نظرة على ما تخبرنا به.

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

نفس المشكلة مثل الحل الأصلي ، ولكن أسوأ قليلا.

الحل الأفضل (ولكن ليس مثاليًا)

عندما كنت أتجول ، وجدت سؤالاً مماثلاً ( في Javascript ، عند إجراء نسخة عميقة ، كيف أتجنب دورة ، بسبب خاصية "هذا"؟ ) لهذا السؤال ، ولكن بطريقة أفضل.

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


يمكنك ببساطة استخدام خاصية الانتشار لنسخ كائن بدون مراجع. ولكن كن حذرًا (راجع التعليقات) ، فإن "النسخة" موجودة فقط على أدنى مستوى كائن / مجموعة. الخصائص المتداخلة لا تزال المراجع!

استنساخ كامل:

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'

جافا سكريبت في الواقع لا يدعم النسخ المستنسخة العميقة أصلا. استخدم وظيفة الأداة المساعدة. على سبيل المثال Ramda:

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


باستخدام Lodash:

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

في ECMAScript 2018

let objClone = { ...obj };

يجب أن تدرك أن الكائنات المتداخلة ما زالت تُنسخ كمرجع.


هنا هي وظيفة يمكنك استخدامها.

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

أردت فقط أن أضيف إلى جميع Object.createالحلول في هذا المنصب ، أن هذا لا يعمل بالطريقة المطلوبة مع nodejs.

في فايرفوكس نتيجة ل

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

هو

{test:"test"} .

في العقدة هو

{}

إجابة جديدة لسؤال قديم! إذا كان لديك متعة في استخدام ECMAScript 2016 (ES6) مع Spread Syntax ، فمن السهل.

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

يوفر هذا أسلوبًا نظيفًا لنسخة ضحلة من كائن ما. إن عمل نسخة عميقة ، بمعنى جعل نسخة جديدة من كل قيمة في كل كائن متداخل متكرر ، يتطلب من الحلول الأثقل المذكورة أعلاه.

جافا سكريبت تتطور.



منذ أن ذكر mindeavor أن الكائن المراد استنساخه هو كائن "مبني حرفيًا" ، قد يكون الحل هو ببساطة إنشاء الكائن عدة مرات بدلاً من استنساخ مثيل الكائن:

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

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

هذا هو تكيف لرمز A. Levy لمعالجة الاستنساخ للوظائف والمراجع المتعددة / الدورية - ما يعنيه ذلك أنه إذا كان هناك خصائص في الشجرة التي تم نسخها ، فإنهما مرجعان لنفس الكائن ، شجرة الشجرة المستنسخة سيكون لها هذه تشير خصائص إلى نفس ونسخة نفس الكائن المشار إليه. هذا أيضا يحل حالة التبعيات الدورية التي ، إذا تركت دون معالجة ، يؤدي إلى حلقة لا نهائية. تعقيد الخوارزمية هو O (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));

يمكنك الرجوع إلى http://www.w3.org/html/wg/drafts/html/master/infrastructure.html#safe-passing-of-structured-data لخوارزمية "تمرير البيانات المنظمة بشكل آمن" من W3C ، والمزمع تنفيذها بواسطة المتصفحات لتمرير البيانات إلى عمال الويب على سبيل المثال. ومع ذلك ، فإنه يحتوي على بعض القيود ، من حيث أنه لا يعالج الوظائف. راجع https://developer.mozilla.org/en-US/docs/DOM/The_structured_clone_algorithm لمزيد من المعلومات ، بما في ذلك خوارزمية بديلة في JS والتي تجعلك جزءًا من الطريق هناك.


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




clone