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

StackOverflow https://stackoverflow.com/questions/122102

  •  02-07-2019
  •  | 
  •  

سؤال

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

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

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

هل كانت مفيدة؟

المحلول

2019-يونيو ملاحظة: كان هذا في الأصل رد على جواب آخر ، ليس الرد المناسب على هذا السؤال.أي فكرة لماذا كان اختيار الإجابة الصحيحة.ولكن منذ القطرية تصاعدت وانها حتى الآن #1 الإجابة على هذا السؤال ، سوف تلخيص الحلول كما ويكي الإجابة.

الأم العميق الاستنساخ

انها تسمى "منظم الاستنساخ" ، يعمل تجريبيا في عقدة 11 وما بعدها, و نأمل أن الأراضي في المتصفحات.انظر هذا الجواب للحصول على مزيد من التفاصيل.

الاستنساخ السريع مع فقدان البيانات - JSON.تحليل/stringify

إذا كنت لا تستخدم Dates, وظائف undefined, Infinity, RegExps, خرائط, مجموعات, النقط, FileLists, ImageDatas ، متفرق المصفوفات ، كتابة المصفوفات أو غيرها من أنواع معقدة داخل كائن واحد بسيط جدا بطانة العميق استنساخ كائن هو:

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'
  re: /.*/,  // lost
}
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()

انظر قربان الجواب على المعايير.

موثوق بها الاستنساخ باستخدام مكتبة

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

  • lodash - cloneDeep;يمكن استيرادها بشكل منفصل عن طريق lodash.clonedeep الوحدة هو الخيار الأفضل إذا كنت لا تستخدم بالفعل مكتبة توفر عميق الاستنساخ وظيفة
  • الزاوي - angular.copy
  • مسج - jQuery.extend(true, { }, oldObject); .clone() إلا استنساخ عناصر DOM

ES6

للتأكد من اكتمالها ، علما أن ES6 تقدم اثنين من نسخة الضحلة آليات: Object.assign() و انتشار المشغل.

نصائح أخرى

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

في بلدي التجارب السابقة حيث السرعة كان الشاغل الرئيسي وجدت

JSON.parse(JSON.stringify(obj))

أن تكون أبطأ طريقة العميق استنساخ كائن (ومن أبطأ من مسج.تمديد مع deep مجموعة العلم الحقيقي بنسبة 10-20%).

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

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

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

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

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

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

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

تحديث ES6

إذا كنت تستخدم جافا سكريبت ES6 تحاول هذه الأم طريقة الاستنساخ أو نسخة الضحلة.

Object.assign({}, obj);

على افتراض أن لديك فقط المتغيرات وليس أي وظائف في وجوه الخاص بك ، يمكنك فقط استخدام:

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

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

HTML القياسية تشمل داخلي منظم الاستنساخ/التسلسل الخوارزمية التي يمكن أن تخلق العميق استنساخ الكائنات.فإنه لا يزال محدودا في بعض المدمج في أنواع, ولكن بالإضافة إلى بعض الأنواع التي يدعمها JSON كما أنها تدعم التواريخ ، RegExps, خرائط, مجموعات, النقط, FileLists, ImageDatas ، متفرق المصفوفات ، كتابة المصفوفات و ربما أكثر في المستقبل.كما أنه يحافظ على المراجع داخل استنساخ البيانات ، مما يتيح لها دعم دورية و متكررة الهياكل التي من شأنها أن تسبب أخطاء JSON.

الدعم في Node.js:التجريبية 🙂

على v8 وحدة في Node.js حاليا (اعتبارا من عقدة 11) يعرض منظم التسلسل API مباشرة, ولكن هذه الوظيفة لا تزال علامة "التجريبية" ، عرضة للتغيير أو إزالة في الإصدارات المستقبلية.إذا كنت تستخدم إصدار متوافق, استنساخ كائن بسيط مثل:

const v8 = require('v8');

const structuredClone = obj => {
  return v8.deserialize(v8.serialize(obj));
};

الدعم المباشر في المتصفحات:ربما في نهاية المطاف ؟ 😐

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

const clone = structuredClone(original);

ما لم يتم شحنها ، المتصفحات' تنظيما استنساخ تطبيقات يتعرض إلا بشكل غير مباشر.

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

انخفاض النفقات العامة وسيلة لخلق منظم استنساخ مع واجهات برمجة التطبيقات القائمة هي نشر البيانات من خلال منفذ واحد من MessageChannels.غيرها من ميناء سوف تنبعث منها 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();

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

على 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();

إذا لم يكن هناك أي مدمج واحد ، قد تتمكن من محاولة:

function clone(obj) {
    if (obj === null || typeof (obj) !== 'object' || 'isActiveClone' in obj)
        return obj;

    if (obj instanceof Date)
        var temp = new obj.constructor(); //or new Date(obj);
    else
        var temp = obj.constructor();

    for (var key in obj) {
        if (Object.prototype.hasOwnProperty.call(obj, key)) {
            obj['isActiveClone'] = null;
            temp[key] = clone(obj[key]);
            delete obj['isActiveClone'];
        }
    }
    return temp;
}

طريقة فعالة إلى استنساخ(ليست عميقة-استنساخ) كائن في سطر واحد من التعليمات البرمجية

وهو Object.assign الأسلوب هو جزء من ECMAScript 2015 (ES6) معيار يفعل بالضبط ما تحتاجه.

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

الكائن.تعيين() يتم استخدام طريقة نسخ القيم من جميع enumerable خصائص خاصة بها من مصدر واحد أو أكثر من الأجسام إلى الكائن الهدف.

اقرأ المزيد...

على 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;
    }
  });
}

كود:

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

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

  • انتداب "=" (صفائف سلسلة, عدد المصفوفات فقط)
  • شريحة (صفائف سلسلة, عدد المصفوفات فقط)
  • سلسلة (سلسلة المصفوفات, عدد المصفوفات فقط)
  • وظيفة مخصصة:على حلقة أو نسخ متكررة
  • مسج دولار.تمديد
  • JSON.تحليل (صفائف سلسلة, عدد المصفوفات ، الكائن المصفوفات فقط)
  • Underscore.js's _.استنساخ (صفائف سلسلة, عدد المصفوفات فقط)
  • لو داش _.cloneDeep

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

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

حيث الانتداب أسرع الأداء:

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

و .شريحة() لديها أداء أفضل .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'}];

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

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

حيث مسج دولار.تمديد أفضل أداء:

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

Object.defineProperty( Object.prototype, "clone", {value: clone, enumerable: false});

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

يمكنك العثور على الآلية الوقائية الوطنية, أيضا.أنها يمكن أن تستخدم في المتصفح وكذلك Node.js.

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

تثبيته مع

npm install clone

أو حزمة مع اندر.

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

(تنويه:أنا صاحب المكتبة.)

أعرف أن هذا هو آخر العمر, ولكن أعتقد أن هذا قد يكون من بعض المساعدة إلى الشخص التالي الذي يتعثر على طول.

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

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

Cloning كائن كان دائما مصدر قلق في JS, ولكن كان كل ذلك قبل ES6 لقد قائمة طرق مختلفة من نسخ كائن في جافا سكريبت أدناه ، تخيل أن لديك كائن أدناه وترغب في الحصول على نسخة عميق من أن:

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.stringify.

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

3) AngularJs:

var  deepCopyObj = angular.copy(obj);

4) مسج:

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

5) UnderscoreJs & Loadash:

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

نأمل أن تكون هذه المساعدة...

إذا كنت تستخدم ذلك ، Underscore.js المكتبة استنساخ الأسلوب.

var newObject = _.clone(oldObject);

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

1.باستخدام جسون.تحليل(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.باستخدام لو داش _.cloneDeep الرابط 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.باستخدام كائن.تعيين طريقة()

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 _.استنساخ الرابط 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

JSBEN.CH قياس الأداء ملعب 1~3 http://jsben.ch/KVQLd Performance Deep copying objects in JavaScript

وهنا نسخة من 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 المكتبة.

تحرير:

هنا هو نسخة أكثر قوة (بفضل جاستن McCandless هذا الآن يدعم دوري المراجع وكذلك):

/**
 * 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://stackoverflow.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();
    };
}

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

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

Lodash لطيفة _.cloneDeep(قيمة) الطريقة:

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

var deep = _.cloneDeep(objects);
console.log(deep[0] === objects[0]);
// => false
function clone(obj)
 { var clone = {};
   clone.prototype = obj.prototype;
   for (property in obj) clone[property] = obj[property];
   return clone;
 }

Crockford يوحي (و أنا أفضل) باستخدام هذه الدالة:

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

نسخة الضحلة بطانة واحدة (ECMAScript 5th edition):

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 6th edition, 2015):

var origin = { foo : {} };
var copy = Object.assign({}, origin);

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

فقط لأنني لم أرى AngularJS ذكر ظننت أن الناس قد تريد أن تعرف...

angular.copy يوفر أيضا وسيلة عميقة نسخ الكائنات والمصفوفات.

يبدو أن هناك أي مثالية عميقة استنساخ المشغل بعد مجموعة مثل الكائنات.كما رمز أدناه يوضح, John Resig هو مسج شبيه يتحول المصفوفات مع غير رقمية في خصائص الكائنات التي لا المصفوفات ، 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)

لدي اثنين من إجابات جيدة اعتمادا على ما إذا كان هدفكم هو استنساخ "القديم عادي كائن جافا سكريبت" أو لا.

دعونا نفترض أيضا أن لديك نية لإنشاء استنساخ كاملة مع أي نموذج إشارات إلى الكائن المصدر.إذا كنت لا ترغب في استنساخ كاملة, ثم يمكنك استخدام العديد من وجوه.استنساخ() الروتينية المقدمة في بعض إجابات أخرى (Crockford نمط).

القديم عادي كائنات جافا سكريبت, مجربة وحقيقية طريقة جيدة استنساخ كائن في الحديث أوقات التشغيل هو بكل بساطة:

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

لاحظ أن الكائن المصدر يجب أن يكون محض كائن JSON.هذا هو القول كل من تداخل خصائص يجب أن يكون scalars (مثل منطقية, سلسلة, مجموعة, موضوع, إلخ).أي وظائف أو الأشياء الخاصة مثل RegExp أو التاريخ لن يكون المستنسخة.

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

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

الآن غير عادي كائنات جافا سكريبت, ليس هناك إجابة بسيطة جدا.في الواقع, لا يمكن أن يكون بسبب الطبيعة الديناميكية وظائف JavaScript الداخلية وجوه الدولة.عميق الاستنساخ سلمان هيكل مع وظائف داخل يتطلب منك إعادة تلك الوظائف الداخلية السياق.و جافا سكريبت ببساطة لا تملك وسيلة موحدة من فعل ذلك.

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

نحن المكتوبة الخاصة بنا ، ولكن أفضل النهج العام الذي رأيته هو تغطية هنا:

http://davidwalsh.name/javascript-clone

هذا هو فكرة الحق.الكاتب (ديفيد وولش) قد علق بها استنساخ وظائف عامة.هذا هو شيء قد ترغب في القيام بها ، اعتمادا على حالة الاستخدام.

الفكرة الرئيسية هو أن تحتاج إلى مقبض خاص إنشاء مثيل من الوظائف (أو prototypal الطبقات ، إذا جاز التعبير) لكل نوع على حدة.هنا هو عدد قليل من الأمثلة RegExp والتاريخ.

ليس هذا فقط هو رمز مختصر, لكنه أيضا جدا للقراءة.فإنه من السهل جدا أن تمتد.

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

لذلك هناك تذهب.النهجين.وكلاهما فعال في رأيي.

هذا ليس عموما الحل الأكثر فعالية ، ولكنه ما أحتاج.اختبار بسيط الحالات أدناه...

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

AngularJS

حسنا إذا كنت تستخدم الزاوي يمكنك أن تفعل ذلك أيضا

var newObject = angular.copy(oldObject);

أنا أختلف مع الإجابة مع أكبر قدر من الأصوات هنا.A عودي استنساخ العميق هو أسرع بكثير من JSON.تحليل(JSON.stringify(الكائنات)) النهج المذكور.

وهنا وظيفة للإشارة سريعة:

function cloneDeep (o) {
  let newO
  let i

  if (typeof o !== 'object') return o

  if (!o) return o

  if (Object.prototype.toString.apply(o) === '[object Array]') {
    newO = []
    for (i = 0; i < o.length; i += 1) {
      newO[i] = cloneDeep(o[i])
    }
    return newO
  }

  newO = {}
  for (i in o) {
    if (o.hasOwnProperty(i)) {
      newO[i] = cloneDeep(o[i])
    }
  }
  return newO
}
// 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;
};

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

هنا هو شامل استنساخ() الأسلوب الذي يمكن استنساخ أي كائن جافا سكريبت.فإنه يعالج تقريبا جميع الحالات:

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;
};
مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top