مسار - ما هي الطريقة الأكثر فعالية لاستنساخ كائن ما في JavaScript؟




مسار جافا سكربت (20)

ما هي الطريقة الأكثر فعالية لاستنساخ كائن JavaScript؟ لقد رأيت obj = eval(uneval(o)); قيد الاستخدام ، ولكن هذا غير قياسي ولا يدعمه سوى Firefox .

لقد فعلت أشياء مثل obj = JSON.parse(JSON.stringify(o)); لكن السؤال الكفاءة.

لقد رأيت أيضا وظائف نسخ متكررة مع عيوب مختلفة.
أنا مندهش لا يوجد حل قانوني موجود.

https://code.i-harness.com


ملاحظة: هذا رد على إجابة أخرى ، وليس رداً مناسباً على هذا السؤال. إذا كنت ترغب في الحصول على استنساخ سريع للجسم ، يرجى اتباع نصيحة كوربان في إجابته عن هذا السؤال.

أريد أن أشير إلى أن طريقة .clone() في jQuery .clone() فقط عناصر DOM. لاستنساخ كائنات JavaScript ، يمكنك القيام بما يلي:

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

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

يمكن العثور على مزيد من المعلومات في وثائق jQuery .

أود أيضًا أن أشير إلى أن النسخة العميقة هي في الواقع أكثر ذكاءً مما هو موضح أعلاه - إنها قادرة على تجنب العديد من الفخاخ (محاولة توسيع عنصر DOM بشكل عميق ، على سبيل المثال). يتم استخدامه في كثير من الأحيان في jQuery الأساسية وفي الإضافات إلى تأثير كبير.


الاستنساخ المنظم

يعرّف HTML5 خوارزمية الاستنساخ الداخلية "المنظمة" التي يمكن أن تنشئ نسخًا عميقة من الكائنات. لا يزال يقتصر على أنواع مدمجة معينة ، ولكن بالإضافة إلى الأنواع القليلة التي تدعمها JSON ، فإنه يدعم أيضًا التواريخ ، و RegExps ، والخرائط ، و المجموعات ، و Blobs ، و FileLists ، و ImageDatas ، و Arrays المتفرقة ، و Typed Arrays ، وربما أكثر في المستقبل. . كما يحافظ على المراجع داخل البيانات المستنسخة ، مما يسمح لها بدعم الهياكل الدورية والعودية التي من شأنها أن تسبب أخطاء لـ JSON.

الدعم المباشر في المتصفحات: قريباً؟ 🙂

لا توفر المتصفحات حاليًا واجهة مباشرة لخوارزمية الاستنساخ المنظمة ، ولكن تتم مناقشة وظيفة كليًا منسقة structuredClone() بشكل فعال في whatwg / html # 793 على GitHub وقد يتم طرحها قريبًا! وكما هو مقترح حاليًا ، فإن استخدامه لمعظم الأغراض سيكون بسيطًا مثل:

const clone = structuredClone(original);

وإلى أن يتم شحن هذا التطبيق ، لا يتم عرض عمليات استنساخ المستعرضات المهيكلة إلا بشكل غير مباشر.

غير متزامن الحل البديل: قابل للاستخدام. 😕

تعد الطريقة الأقل ارتفاعًا لإنشاء نسخة مركبة مع واجهات برمجة التطبيقات الموجودة هي نشر البيانات من خلال منفذ واحد من MessageChannels . سوف .data المنفذ الآخر حدثًا message باستنساخ منظم .data . لسوء الحظ ، الإصغاء لهذه الأحداث هو بالضرورة غير متزامن ، وتكون البدائل المتزامنة أقل عملية.

class StructuredCloner {
  constructor() {
    this.pendingClones_ = new Map();
    this.nextKey_ = 0;

    const channel = new MessageChannel();
    this.inPort_ = channel.port1;
    this.outPort_ = channel.port2;

    this.outPort_.onmessage = ({data: {key, value}}) => {
      const resolve = this.pendingClones_.get(key);
      resolve(value);
      this.pendingClones_.delete(key);
    };
    this.outPort_.start();
  }

  cloneAsync(value) {
    return new Promise(resolve => {
      const key = this.nextKey_++;
      this.pendingClones_.set(key, resolve);
      this.inPort_.postMessage({key, value});
    });
  }
}

const structuredCloneAsync = window.structuredCloneAsync =
    StructuredCloner.prototype.cloneAsync.bind(new StructuredCloner);

استخدام المثال:

const main = async () => {
  const original = { date: new Date(), number: Math.random() };
  original.self = original;

  const clone = await structuredCloneAsync(original);

  // They're different objects:
  console.assert(original !== clone);
  console.assert(original.date !== clone.date);

  // They're cyclical:
  console.assert(original.self === original);
  console.assert(clone.self === clone);

  // They contain equivalent values:
  console.assert(original.number === clone.number);
  console.assert(Number(original.date) === Number(clone.date));

  console.log("Assertions complete.");
};

main();

الحلول المتزامنة: فظيعة! 🤢

لا توجد خيارات جيدة لإنشاء نسخ مستنسخة مهيكلة بشكل متزامن. وهنا بضعة من الخارقة غير عملي بدلا من ذلك.

ينشئ كل من history.pushState() و history.replaceState() نسخة متماثلة من وسيطتهم الأولى ، وتعيين تلك القيمة إلى history.state . يمكنك استخدام هذا لإنشاء نسخة مركبة من أي كائن مثل هذا:

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

استخدام المثال:

'use strict';

const main = () => {
  const original = { date: new Date(), number: Math.random() };
  original.self = original;

  const clone = structuredClone(original);
  
  // They're different objects:
  console.assert(original !== clone);
  console.assert(original.date !== clone.date);

  // They're cyclical:
  console.assert(original.self === original);
  console.assert(clone.self === clone);

  // They contain equivalent values:
  console.assert(original.number === clone.number);
  console.assert(Number(original.date) === Number(clone.date));
  
  console.log("Assertions complete.");
};

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

main();

على الرغم من تزامن ذلك ، يمكن أن يكون بطيئًا جدًا. إنه يتحمل كل النفقات العامة المرتبطة بمعالجة سجل المتصفح. يمكن أن يتسبب استدعاء هذه الطريقة بشكل متكرر في عدم استجابة Chrome مؤقتًا.

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

const structuredClone = obj => {
  const n = new Notification('', {data: obj, silent: true});
  n.onshow = n.close.bind(n);
  return n.data;
};

استخدام المثال:

'use strict';

const main = () => {
  const original = { date: new Date(), number: Math.random() };
  original.self = original;

  const clone = structuredClone(original);
  
  // They're different objects:
  console.assert(original !== clone);
  console.assert(original.date !== clone.date);

  // They're cyclical:
  console.assert(original.self === original);
  console.assert(clone.self === clone);

  // They contain equivalent values:
  console.assert(original.number === clone.number);
  console.assert(Number(original.date) === Number(clone.date));
  
  console.log("Assertions complete.");
};

const structuredClone = obj => {
  const n = new Notification('', {data: obj, silent: true});
  n.close();
  return n.data;
};

main();


AngularJS

حسنًا ، إذا كنت تستخدم الزاوي ، فيمكنك القيام بذلك أيضًا

var newObject = angular.copy(oldObject);

أعرف أن هذه رسالة قديمة ، لكنني أعتقد أن هذا قد يكون مفيدًا للشخص التالي الذي يتعثر.

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

var a = function(){
    return {
        father:'zacharias'
    };
},
b = a(),
c = a();
c.father = 'johndoe';
alert(b.father);

الخروج من هذا المعيار: http://jsben.ch/#/bWfk9

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

JSON.parse(JSON.stringify(obj))

لتكون أسرع طريقة لاستنساخ كائن ما ( jQuery.extend مع وضع علم عميق صحيحًا بنسبة 10-20٪).

jQuery.extend سريع جدا عندما يتم تعيين العلم العميق إلى false (استنساخ ضحل). إنه خيار جيد ، لأنه يتضمن بعض المنطق الإضافي للتحقق من النوع ولا ينسخ على خصائص غير محددة ، وما إلى ذلك ، ولكن هذا سيؤدي إلى تباطؤك قليلاً.

إذا كنت تعرف بنية الكائنات التي تحاول استنساخها أو يمكنك تجنب المصفوفات المتداخلة العميقة ، يمكنك كتابة حلقة بسيطة for (var i in obj) لاستنساخ كائنك أثناء التحقق من خاصية onOwenProperty وسيكون أسرع بكثير من jQuery.

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

تمتص محركات التتبع لجافا سكريبت في التحسين ل for..in الحلقات والتدقيق hasOwnProperty سوف تبطئك كذلك. استنساخ يدوي عندما تكون السرعة ضرورة مطلقة.

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

احذر من استخدام JSON.parse(JSON.stringify(obj)) على كائنات Date - JSON.stringify(new Date()) بإرجاع تمثيل سلسلة التاريخ بتنسيق ISO ، الذي لا يقوم JSON.parse() بتحويله إلى كائن Date . انظر هذا الجواب لمزيد من التفاصيل .

بالإضافة إلى ذلك ، يُرجى ملاحظة أنه ، في Chrome 65 على الأقل ، لا يعد الاستنساخ الأصلي وسيلة للتحايل. وفقًا لـ JSPerf ، فإن أداء الاستنساخ الأصلي عن طريق إنشاء وظيفة جديدة يكون أبطأ بنحو 800x من استخدام JSON.stringify وهو سريع بشكل لا يصدق على جميع الأصعدة.


الشفرة:

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

هناك مكتبة (تسمى "clone") ، والتي تقوم بذلك بشكل جيد. فهو يوفر الاستنساخ / الاستنساخ الأكثر تكرارا للأشياء التعسفية التي أعرفها. كما أنه يدعم المراجع الدائرية ، التي لا تغطيها الإجابات الأخرى ، حتى الآن.

يمكنك العثور عليها على npm أيضًا. يمكن استخدامه للمتصفح وكذلك Node.js.

في ما يلي مثال على كيفية استخدامه:

تثبيته مع

npm install clone

أو Ender مع Ender .

ender build clone [...]

يمكنك أيضًا تنزيل شفرة المصدر يدويًا.

ثم يمكنك استخدامه في التعليمات البرمجية المصدر الخاص بك.

var clone = require('clone');

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

console.log(a);                   // { foo: { bar: 'foo' } }
console.log(b);                   // { foo: { bar: 'baz' } }

(إخلاء المسؤولية: أنا مؤلف المكتبة).


نسخ كائنات عميقة في JavaScript (أعتقد الأفضل والأبسط)

1. باستخدام JSON.parse (JSON.stringify (object))؛

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. باستخدام وصلة 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.Using Underscore.js _.clone link 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.)

المرجع medium.com

أداء JISBEN.CH ملعب الأداء 1 ~ 3 http://jsben.ch/KVQLd


نسخة عميقة حسب الأداء: مرتبة من الأفضل إلى الأسوأ

  • إعادة تعيين "=" (صفائف السلسلة ، صفائف الأرقام - فقط)
  • شريحة (صفائف السلسلة ، صفائف الأرقام - فقط)
  • تسلسل (صفائف السلسلة ، صفائف الأرقام - فقط)
  • وظيفة مخصصة: عن حلقة أو نسخة متكررة
  • jQuery's $ .extend
  • JSON.parse (صفائف السلسلة ، صفائف الأرقام ، صفائف الكائن - فقط)
  • Underscore.js 's _.clone (صفائف السلسلة ، صفائف الأرقام - فقط)
  • Lo-Dash's _.cloneDeep

نسخ عميق لمجموعة من السلاسل أو الأرقام (مستوى واحد - بدون مؤشرات مرجعية):

عندما يحتوي مصفوفة على أرقام وسلاسل - وظائف مثل .slice () و .concat () و .splice () ووظيفة التعيين "=" ووظيفة نسخ Underscore.js's؛ سيجعل نسخة عميقة من عناصر المجموعة.

حيث يكون لإعادة التعيين الأداء الأسرع:

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

و .slice () لديه أداء أفضل من .concat () ، http://jsperf.com/duplicate-array-slice-vs-concat/3

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

نسخ عميق لمجموعة من الكائنات (مستويين أو أكثر - مؤشرات مرجعية):

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

اكتب وظيفة مخصصة (لديها أداء أسرع من $ .extend () أو JSON.parse):

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

copy(arr1);

استخدم دالات الأداة المساعدة لجهة خارجية:

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

حيث يكون ل $ .extend في jQuery أداء أفضل:


وإليك إصدار من إجابة ConroyP أعلاه يعمل حتى إذا كان المُنشئ قد طلب معلمات:

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

إنه مقتضب ، ويعمل كما هو متوقع ولا تحتاج إلى مكتبة.

تصحيح:

هذا هو polyfill 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

إذا كنت تستخدمها ، فإن مكتبة Underscore.js تحتوي على طريقة clone .

var newObject = _.clone(oldObject);

بالنسبة للأشخاص الذين يرغبون في استخدام 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;
  });
}

لدى Lodash أسلوب lodash.com/docs#cloneDeep لطيف :

var objects = [{ 'a': 1 }, { 'b': 2 }];

var deep = _.cloneDeep(objects);
console.log(deep[0] === objects[0]);
// => false

نسخة من الخطوط المائلة الضحلة ( النسخة الخامسة من ECMAScript ):

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

console.log(origin, copy);
console.log(origin == copy); // false
console.log(origin.foo == copy.foo); // true

ونسخة ضحلة واحدة (نسخة ECMAScript 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

هنا طريقة clone () شاملة يمكن استنساخ أي كائن JavaScript. يتعامل مع جميع الحالات تقريبا:

function clone(src, deep) {

    var toString = Object.prototype.toString;
    if (!src && typeof src != "object") {
        // Any non-object (Boolean, String, Number), null, undefined, NaN
        return src;
    }

    // Honor native/custom clone methods
    if (src.clone && toString.call(src.clone) == "[object Function]") {
        return src.clone(deep);
    }

    // DOM elements
    if (src.nodeType && toString.call(src.cloneNode) == "[object Function]") {
        return src.cloneNode(deep);
    }

    // Date
    if (toString.call(src) == "[object Date]") {
        return new Date(src.getTime());
    }

    // RegExp
    if (toString.call(src) == "[object RegExp]") {
        return new RegExp(src);
    }

    // Function
    if (toString.call(src) == "[object Function]") {

        //Wrap in another method to make sure == is not true;
        //Note: Huge performance issue due to closures, comment this :)
        return (function(){
            src.apply(this, arguments);
        });
    }

    var ret, index;
    //Array
    if (toString.call(src) == "[object Array]") {
        //[].slice(0) would soft clone
        ret = src.slice();
        if (deep) {
            index = ret.length;
            while (index--) {
                ret[index] = clone(ret[index], true);
            }
        }
    }
    //Object
    else {
        ret = src.constructor ? new src.constructor() : {};
        for (var prop in src) {
            ret[prop] = deep
                ? clone(src[prop], true)
                : src[prop];
        }
    }
    return ret;
};

يبدو أنه لا يوجد مشغل مثالي للاستنساخ العميق حتى الآن للأجسام المشابهة للمصفوفة. كما يوضح الكود أدناه ، يقوم jQuery John Resig بتحويل المصفوفات بخصائص غير رقمية إلى كائنات ليست ذات صفائف ، ويقوم JSON cloner الخاص بـ RegDwight بإسقاط الخصائص غير الرقمية. توضح الاختبارات التالية هذه النقاط على متصفحات متعددة:

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