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




clone (25)

نسخة من الخطوط المائلة الضحلة ( النسخة الخامسة من 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

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

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

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


فقط لأنني لم أر AngularJS المذكورة ، ويعتقد أن الناس قد ترغب في معرفة ...

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


أنا لا أوافق على الإجابة بأعلى الأصوات here . A العودية عميق استنساخ هو أسرع بكثير من JSON.parse (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
}

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

var newObject = _.clone(oldObject);

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

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

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

هنا طريقة 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;
};

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.parse و 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

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


الشفرة:

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

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

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

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

AngularJS

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

var newObject = angular.copy(oldObject);

وإليك إصدار من إجابة 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();
    };
}

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

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

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

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

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

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

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

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

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

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

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

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

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

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

يعرّف 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();


نسخ كائنات عميقة في 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


هناك مكتبة (تسمى "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' } }

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


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

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

الخروج من هذا المعيار: 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 وهو سريع بشكل لا يصدق على جميع الأصعدة.


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

نعم ، MemberwiseClone يجعل نسخة ضحلة ، ولكن عكس MemberwiseClone ليس Clone ؛ قد يكون ، ربما ، DeepClone ، غير موجود. عندما تستخدم كائنًا من خلال واجهة ICloneable ، لا يمكنك معرفة نوع الاستنساخ الذي يقوم به الكائن الأساسي. (ولن توضح تعليقات XML ، لأنك ستحصل على تعليقات الواجهة بدلاً من تلك الموجودة في طريقة نسخ الكائن.)

ما أفعله عادة هو ببساطة جعل طريقة Copy هي ما أريده بالضبط.





javascript object clone