Underscore: تمديد عميق ونسخ عميق

تم إنشاؤها على ٢٥ مارس ٢٠١١  ·  28تعليقات  ·  مصدر: jashkenas/underscore

طلب المواصفات:
هل من الممكن أن يكون لديك معلمة منطقية على _.extend() و _.copy() لجعلها عميقة ، أو أن يكون لديك طرق عميقة منفصلة؟

enhancement wontfix

التعليق الأكثر فائدة

_.deepClone = function(obj) {
      return (!obj || (typeof obj !== 'object'))?obj:
          (_.isString(obj))?String.prototype.slice.call(obj):
          (_.isDate(obj))?new Date(obj.valueOf()):
          (_.isFunction(obj.clone))?obj.clone():
          (_.isArray(obj)) ? _.map(obj, function(t){return _.deepClone(t)}):
          _.mapObject(obj, function(val, key) {return _.deepClone(val)});
  };

ال 28 كومينتر

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

صحيح ، النسخ العميقة هي بالتأكيد فوضوية في جافا سكريبت. يجب أن تكون هناك نقطة تقول فيها إن الفوائد تفوق الأضرار ، أليس كذلك؟ إن جعل الناس يكتبون باستمرار أساليب نسخ عميقة رديئة لا تعمل كما هو متوقع هو أسوأ من استخدام طريقة النسخ العميق التي تم ترميزها بعناية ، ولكن بها قيود موثقة جيدًا. على سبيل المثال ، حل هذا الرجل رائع: http://stackoverflow.com/questions/728360/copying-an-object-in-javascript/728694#728694

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

نعم - وحتى حل هذا الرجل في الحقيقة ليس جيدًا بما يكفي. إذا لم نتمكن من تنفيذها بشكل صحيح ، فلا يجب أن ننفذها.

ولكن هل من الأفضل حقًا للمستخدمين طرح تطبيقهم الخاص ، وربما تعطله بشكل أكبر؟

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

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

يحتوي Jquery.extend على خيار عميق.

+1 للخيار العميق ، بغض النظر عن مدى صعوبة تنفيذه.

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

kmalakoff كتب تنفيذًا ، يجب أن تعطيه بعض التعليقات! :)

يمكنك حتى دمج _.cloneToDepth الأصلي الخاص بي مع _clone وإضافة معلمة عمق ....

  // Create a duplicate of a container of objects to any zero-indexed depth.
  _.cloneToDepth = _.clone = function(obj, depth) {
    if (typeof obj !== 'object') return obj;
    var clone = _.isArray(obj) ? obj.slice() : _.extend({}, obj);
    if (!_.isUndefined(depth) && (depth > 0)) {
      for (var key in clone) {
        clone[key] = _.clone(clone[key], depth-1);
      }
    }
    return clone;
  };

أيضًا ، كتبت _.own و _.disown والتي تقدم اتفاقية للملكية (إما أزواج من الاحتفاظ / الإصدار أو الاستنساخ / التدمير). إنه يتكرر مستوى واحد فقط لأسفل ، لكنني أفترض أنه يمكن أن يكون هناك خيار مضاف للتكرار الكلي (أريد أن أرى حالة استخدام!).

  _.own = function(obj, options) {
    if (!obj || (typeof(obj)!='object')) return obj;
    options || (options = {});
    if (_.isArray(obj)) {
      if (options.share_collection) { _.each(obj, function(value) { _.own(value, {prefer_clone: options.prefer_clone}); }); return obj; }
      else { var a_clone =  []; _.each(obj, function(value) { a_clone.push(_.own(value, {prefer_clone: options.prefer_clone})); }); return a_clone; }
    }
    else if (options.properties) {
      if (options.share_collection) { _.each(obj, function(value, key) { _.own(value, {prefer_clone: options.prefer_clone}); }); return obj; }
      else { var o_clone = {}; _.each(obj, function(value, key) { o_clone[key] = _.own(value, {prefer_clone: options.prefer_clone}); }); return o_clone; }
    }
    else if (obj.retain) {
      if (options.prefer_clone && obj.clone) return obj.clone();
      else obj.retain();
    }
    else if (obj.clone) return obj.clone();
    return obj;
  };

  _.disown = function(obj, options) {
    if (!obj || (typeof(obj)!='object')) return obj;
    options || (options = {});
    if (_.isArray(obj)) {
      if (options.clear_values) { _.each(obj, function(value, index) { _.disown(value); obj[index]=null; }); return obj; }
      else {
        _.each(obj, function(value) { _.disown(value); });
        obj.length=0; return obj;
      }
    }
    else if (options.properties) {
      if (options.clear_values) { _.each(obj, function(value, key) { _.disown(value); obj[key]=null; }); return obj; }
      else {
        _.each(obj, function(value) { _.disown(value); });
        for(key in obj) { delete obj[key]; }
        return obj;
      }
    }
    else if (obj.release) obj.release();
    else if (obj.destroy) obj.destroy();
    return obj;
  };

وإذا كنت تريد غرضًا عامًا ، استنساخ قابل للتوسيع:

  // Create a duplicate of all objects to any zero-indexed depth.
  _.deepClone = function(obj, depth) {
    if (typeof obj !== 'object') return obj;
    if (_.isString(obj)) return obj.splice();
    if (_.isDate(obj)) return new Date(obj.getTime());
    if (_.isFunction(obj.clone)) return obj.clone();
    var clone = _.isArray(obj) ? obj.slice() : _.extend({}, obj);
    if (!_.isUndefined(depth) && (depth > 0)) {
      for (var key in clone) {
        clone[key] = _.deepClone(clone[key], depth-1);
      }
    }
    return clone;
  };

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

لم أكن راضيًا تمامًا عن إجابتي بالأمس (لا يجب أن أكتب الكود بعد منتصف الليل) ... لقد توصلت إلى نسختين ( _.cloneToDepth بشكل أساسي يقوم فقط باستنساخ الحاوية ، مع الاحتفاظ بالمراجع للكائنات الأصلية و _.deepClone الذي ينسخ المثيلات):

  // Create a duplicate of a container of objects to any zero-indexed depth.
  _.cloneToDepth = _.containerClone = _.clone = function(obj, depth) {
    if (!obj || (typeof obj !== 'object')) return obj;  // by value
    var clone;
    if (_.isArray(obj)) clone = Array.prototype.slice.call(obj);
    else if (obj.constructor!=={}.constructor) return obj; // by reference
    else clone = _.extend({}, obj);
    if (!_.isUndefined(depth) && (depth > 0)) {
      for (var key in clone) {
        clone[key] = _.clone(clone[key], depth-1);
      }
    }
    return clone;
  };

  // Create a duplicate of all objects to any zero-indexed depth.
  _.deepClone = function(obj, depth) {
    if (!obj || (typeof obj !== 'object')) return obj;  // by value
    else if (_.isString(obj)) return String.prototype.slice.call(obj);
    else if (_.isDate(obj)) return new Date(obj.valueOf());
    else if (_.isFunction(obj.clone)) return obj.clone();
    var clone;
    if (_.isArray(obj)) clone = Array.prototype.slice.call(obj);
    else if (obj.constructor!=={}.constructor) return obj; // by reference
    else clone = _.extend({}, obj);
    if (!_.isUndefined(depth) && (depth > 0)) {
      for (var key in clone) {
        clone[key] = _.deepClone(clone[key], depth-1);
      }
    }
    return clone;
  };

كما يشير michaelficarra ، قد تكون حالات الاستخدام غير واضحة. أنا شخصياً أستخدم:

1) _.own / _.disown عندما أرغب في مشاركة العناصر التي تحتوي على دورات حياة معقدة و / أو نماذج ملكية (مثل حساب المرجع للتعامل مع التنظيف بشكل صحيح)
2) لقد استخدمت _.cloneToDepth / _.containerClone (نادرًا!) عندما كان لدي خيارات معقدة ومتداخلة لوظيفة ما.
3) لم أحتاج أبدًا إلى _.deepClone ، لكن افترض أنه قد يكون مفيدًا كطريقة عامة _.clone إذا كنت تكتب دالة عامة تدعم الأنواع بمرونة (على سبيل المثال ، لا أفعل أهتم بما تمرره لي ، لكنني سأقوم بتعديله ولا أريد أن يكون له آثار جانبية على الأصل - على الرغم من أن السلاسل هي حالة خاصة غير قابلة للتغيير).

لقد أرسلت الكود والاختبارات هنا: https://github.com/kmalakoff/underscore-awesomer/commit/0cf6008f16ad6e6daf60caf456021693ef33fec5

ملحوظة: أولئك العالقون في البحث عن استنساخ عميق غير مكتمل من سطر واحد لكائنات بسيطة ذات دلالات يمكن التنبؤ بها يمكنهم فقط JSON.parse(JSON.stringify(object)) .

adamhooper هل تميل هذه الطريقة إلى فقدان الخصائص أو شيء من هذا القبيل؟ لماذا هو غير مكتمل؟

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

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

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

لقد قمت بتحويل مزيج Kurt Milam's deepExtend إلى حزمة npm.

https://github.com/pygy/undescoreDeepExtend/

michaelficarra ، يرجى توضيح كيف أن النسخ العميق ليس حلاً جيدًا لاستنساخ بنية شجرة عامة؟

لجميع من يحاولون استخدام نسخة عميقة من سطر واحد adamhooper ، يجب أن تدرك أنها لا تعمل للتواريخ
JSON.parse (JSON.stringify (كائن))
في الواقع فإنه يحول أي كائن تاريخ إلى سلسلة

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

في الواقع ، يوجد مكان للنسخ العميق ، ولكن يجب أن يتزامن مع فحص النوع ، لذا فهي حالة استخدام محددة جدًا في رأيي ، وليست الغرض العام. مناسب بشكل أفضل لمكتبات مخططات JSON أو برامج تحميل التكوين. ليس حزام أداة جافا سكريبت.

في معظم الحالات ، _.extend({}, obj1, { prop1: 1, prop2: 2 }) هو ما أحتاج فعله حقًا وهو:

  • يعطيني شيئًا جديدًا
  • لا يقوم بتعديل كائن المصدر الخاص بي و
  • ليس نسخة عميقة
_.deepClone = function(obj) {
      return (!obj || (typeof obj !== 'object'))?obj:
          (_.isString(obj))?String.prototype.slice.call(obj):
          (_.isDate(obj))?new Date(obj.valueOf()):
          (_.isFunction(obj.clone))?obj.clone():
          (_.isArray(obj)) ? _.map(obj, function(t){return _.deepClone(t)}):
          _.mapObject(obj, function(val, key) {return _.deepClone(val)});
  };

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

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

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

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

هل كانت هذه الصفحة مفيدة؟
0 / 5 - 0 التقييمات

القضايا ذات الصلة

githublyp picture githublyp  ·  3تعليقات

umarfarooq125 picture umarfarooq125  ·  8تعليقات

Francefire picture Francefire  ·  5تعليقات

arypbatista picture arypbatista  ·  3تعليقات

zackschuster picture zackschuster  ·  5تعليقات