javascript - معلم - شرح جافا سكريبت




التحميل الزائد للوظيفة في Javascript-أفضل الممارسات (20)

ما أفضل طريقة (طرق) لتزييف الحمل الزائد في جافا سكريبت؟

أعلم أنه من غير الممكن زيادة الوظائف في جافا سكريبت كما هو الحال في اللغات الأخرى. إذا كنت بحاجة إلى وظيفة باستخدام اثنين من الاستخدامات foo(x) و foo(x,y,z) وهي الطريقة المفضلة / المفضلة:

  1. استخدام أسماء مختلفة في المقام الأول
  2. استخدام وسيطات اختيارية مثل y = y || 'default' y = y || 'default'
  3. استخدام عدد من الحجج
  4. التحقق من أنواع الحجج
  5. أو كيف؟

إذا كنت بحاجة إلى وظيفة باستخدام اثنين من الاستخدامات foo (x) و foo (x، y، z) وهي الطريقة المفضلة / المفضلة؟

تكمن المشكلة في أن JavaScript لا تدعم عملية التحميل الزائد للطريقة. لذلك ، إذا رأى / قام بتوزيع وظيفتين أو أكثر بنفس الأسماء فسوف يفكر في آخر وظيفة محددة ويكتب فوق الوظائف السابقة.

واحد من الطريقة التي أعتقد أنها مناسبة لمعظم الحالات هو التالي -

دعنا نقول لديك طريقة

function foo(x)
{
} 

بدلاً من أسلوب التحميل الزائد غير الممكن في javascript ، يمكنك تحديد طريقة جديدة

fooNew(x,y,z)
{
}

ثم تعديل الوظيفة الأولى على النحو التالي -

function foo(arguments)
{
  if(arguments.length==2)
  {
     return fooNew(arguments[0],  arguments[1]);
  }
} 

إذا كان لديك العديد من هذه الأساليب المحملة بشكل زائد ، ففكر في استخدام switch من مجرد عبارات if-else .

( مزيد من التفاصيل )


إعادة توجيه نمط => أفضل الممارسات على الحمولة الزائدة JS

إلى الأمام إلى وظيفة أخرى ، يتم إنشاء الاسم من النقطتين الثالثة والرابعة:

  1. استخدام عدد من الحجج
  2. التحقق من أنواع الحجج
window['foo_'+arguments.length+'_'+Array.from(arguments).map((arg)=>typeof arg).join('_')](...arguments)

التطبيق على قضيتك:

 function foo(){
          return window['foo_'+arguments.length+Array.from(arguments).map((arg)=>typeof arg).join('_')](...arguments);

  }
   //------Assuming that `x` , `y` and `z` are String when calling `foo` . 

  /**-- for :  foo(x)*/
  function foo_1_string(){
  }
  /**-- for : foo(x,y,z) ---*/
  function foo_3_string_string_string(){

  }

عينة معقدة أخرى:

      function foo(){
          return window['foo_'+arguments.length+Array.from(arguments).map((arg)=>typeof arg).join('_')](...arguments);
       }

        /** one argument & this argument is string */
      function foo_1_string(){

      }
       //------------
       /** one argument & this argument is object */
      function foo_1_object(){

      }
      //----------
      /** two arguments & those arguments are both string */
      function foo_2_string_string(){

      }
       //--------
      /** Three arguments & those arguments are : id(number),name(string), callback(function) */
      function foo_3_number_string_function(){
                let args=arguments;
                  new Person(args[0],args[1]).onReady(args[3]);
      }

       //--- And so on ....   

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

  1. استخدام وسيطات اختيارية مثل y = y || "الافتراضي". يعد هذا أمرًا مناسبًا إذا كنت تستطيع القيام بذلك ، ولكن قد لا تعمل دائمًا عمليًا ، على سبيل المثال عندما يكون 0 / null / undefined عبارة عن وسيطة صالحة.

  2. استخدام عدد من الحجج. مشابه للخيار الأخير ولكن قد يعمل عندما لا يعمل # 1.

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

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


أفعل هذا غالبًا:

C #:

public string CatStrings(string p1)                  {return p1;}
public string CatStrings(string p1, int p2)          {return p1+p2.ToString();}
public string CatStrings(string p1, int p2, bool p3) {return p1+p2.ToString()+p3.ToString();}

CatStrings("one");        // result = one
CatStrings("one",2);      // result = one2
CatStrings("one",2,true); // result = one2true

مكافئ جافا سكريبت:

function CatStrings(p1, p2, p3)
{
  var s = p1;
  if(typeof p2 !== "undefined") {s += p2;}
  if(typeof p3 !== "undefined") {s += p3;}
  return s;
};

CatStrings("one");        // result = one
CatStrings("one",2);      // result = one2
CatStrings("one",2,true); // result = one2true

هذا المثال بالذات أكثر أناقة في جافا سكريبت من C #. المعلمات غير المحددة هي "غير محددة" في javascript ، والتي يتم تقييمها إلى false في عبارة if. ومع ذلك ، لا يقوم تعريف الدالة بنقل المعلومات التي تكون p2 و p3 اختيارية. إذا كنت بحاجة إلى الكثير من التحميل الزائد ، فقد قرر jQuery استخدام كائن كمعلمة ، على سبيل المثال ، jQuery.ajax (خيارات). وأنا أتفق معهم على أن هذا هو النهج الأقوى والموثق بوضوح للتحميل الزائد ، لكني نادراً ما أحتاج إلى أكثر من واحد أو اثنين من المعلمات الاختيارية السريعة.

تعديل: تم تغيير اختبار IF حسب اقتراح إيان


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

function f(...rest){   // rest is an array
   console.log(rest.length);
   for (v of rest) if (typeof(v)=="number")console.log(v);
}
f(1,2,3);  // 3 1 2 3

الجواب الصحيح هو أن هناك عدم التحميل في جافا سكريبت.

الفحص / التبديل داخل الوظيفة غير زائد.

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

على سبيل المثال ، doTask () و doTask (كائن O) أساليب overloaded. لاستدعاء هذا الأخير ، يجب تمرير كائن كمعلمة ، بينما لا يتطلب الكائن السابق معلمة ، ويتم استدعاؤه بحقل معلمة فارغ. قد يكون الخطأ الشائع هو تعيين قيمة افتراضية للكائن في الطريقة الثانية ، مما ينتج عنه خطأ في استدعاء غامض ، لأن المحول البرمجي لن يعرف أي من الطريقتين اللتين يستخدمانهما.

https://en.wikipedia.org/wiki/Function_overloading

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


بالنسبة لحالة الاستخدام الخاصة بك ، هذه هي الطريقة التي ES6 معها مع ES6 (حيث إنها بالفعل نهاية عام 2017):

const foo = (x, y, z) => {
  if (y && z) {
    // Do your foo(x, y, z); functionality
    return output;
  }
  // Do your foo(x); functionality
  return output;
}

يمكنك بشكل واضح تكييف هذا للعمل مع أي مقدار من المعلمات وتغيير بياناتك الشرطية وفقًا لذلك.


بما أن هذا المنشور يحتوي بالفعل على الكثير من الحلول المختلفة ظننت أنني أنشر واحدة أخرى.

function onlyUnique(value, index, self) {
    return self.indexOf(value) === index;
}

function overload() {
   var functions = arguments;
   var nroffunctionsarguments = [arguments.length];
    for (var i = 0; i < arguments.length; i++) {
        nroffunctionsarguments[i] = arguments[i].length;
    }
    var unique = nroffunctionsarguments.filter(onlyUnique);
    if (unique.length === arguments.length) {
        return function () {
            var indexoffunction = nroffunctionsarguments.indexOf(arguments.length);
            return functions[indexoffunction].apply(this, arguments);
        }
    }
    else throw new TypeError("There are multiple functions with the same number of parameters");

}

يمكن استخدام هذا كما هو موضح أدناه:

var createVector = overload(
        function (length) {
            return { x: length / 1.414, y: length / 1.414 };
        },
        function (a, b) {
            return { x: a, y: b };
        },
        function (a, b,c) {
            return { x: a, y: b, z:c};
        }
    );
console.log(createVector(3, 4));
console.log(createVector(3, 4,5));
console.log(createVector(7.07));

هذا الحل ليس مثالياً ولكني أريد فقط أن أبين كيف يمكن القيام به.


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

myFunction = function(a, b, c) {
     if (b === undefined && c === undefined ){
          // do x...
     }
     else {
          // do y...
     }
};

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

function sum() {
    var x = 0;
    for (var i = 0; i < arguments.length; ++i) {
        x += arguments[i];
    }
    return x;
}

حتى يمكنك تعديل هذا الرمز إلى:

function sum(){
    var s = 0 ;
    if(typeof arguments[0] !== "undefined") s +=arguments[0] ;
    if(typeof arguments[0] !== "undefined") s +=arguments[0] ;
.
.
.
    return s;
}

لا توجد طريقة لعمل التحميل الزائد في javascript. لذا ، أوصي بما يلي بالطريقة typeof() بدلاً من وظيفة متعددة لتزييف الحمولة الزائدة.

function multiTypeFunc(param)
{
    if(typeof param == 'string') {
        alert("I got a string type parameter!!");
     }else if(typeof param == 'number') {
        alert("I got a number type parameter!!");
     }else if(typeof param == 'boolean') {
        alert("I got a boolean type parameter!!");
     }else if(typeof param == 'object') {
        alert("I got a object type parameter!!");
     }else{
        alert("error : the parameter is undefined or null!!");
     }
}

حظا طيبا وفقك الله!


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


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

TEST = {};

TEST.multiFn = function(){
    // function map for our overloads
    var fnMap = {};

    fnMap[0] = function(){
        console.log("nothing here");
        return this;    //    support chaining
    }

    fnMap[1] = function(arg1){
        //    CODE here...
        console.log("1 arg: "+arg1);
        return this;
    };

    fnMap[2] = function(arg1, arg2){
        //    CODE here...
        console.log("2 args: "+arg1+", "+arg2);
        return this;
    };

    fnMap[3] = function(arg1,arg2,arg3){
        //    CODE here...
        console.log("3 args: "+arg1+", "+arg2+", "+arg3);
        return this;
    };

    console.log("multiFn is now initialized");

    //    redefine the function using the fnMap in the closure
    this.multiFn = function(){
        fnMap[arguments.length].apply(this, arguments);
        return this;
    };

    //    call the function since this code will only run once
    this.multiFn.apply(this, arguments);

    return this;    
};

قم بتجريبه.

TEST.multiFn("0")
    .multiFn()
    .multiFn("0","1","2");

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

    function optionsObjectTest(x, y, opts) {
        opts = opts || {}; // default to an empty options object

        var stringValue = opts.stringValue || "string default value";
        var boolValue = !!opts.boolValue; // coerces value to boolean with a double negation pattern
        var numericValue = opts.numericValue === undefined ? 123 : opts.numericValue;

        return "{x:" + x + ", y:" + y + ", stringValue:'" + stringValue + "', boolValue:" + boolValue + ", numericValue:" + numericValue + "}";

}

here مثال على كيفية استخدام كائن الخيارات


هناك طريقتان يمكنك اتباعهما بشكل أفضل:

  1. قم بتمرير قاموس (صفيف ارتباطي) إذا كنت ترغب في ترك الكثير من المرونة

  2. أخذ كائن كحجة واستخدام الوراثة النموذج الأولي لإضافة المزيد من المرونة.


يمكنك استخدام "addMethod" من John Resig. باستخدام هذه الطريقة ، يمكنك استخدام أساليب "التحميل الزائد" استنادًا إلى عدد الحجج.

// addMethod - By John Resig (MIT Licensed)
function addMethod(object, name, fn){
    var old = object[ name ];
    object[ name ] = function(){
        if ( fn.length == arguments.length )
            return fn.apply( this, arguments );
        else if ( typeof old == 'function' )
            return old.apply( this, arguments );
    };
}

لقد قمت أيضًا بإنشاء بديل لهذه الطريقة التي تستخدم التخزين المؤقت للاحتفاظ بتنويعات الدالة. يتم وصف الاختلافات هنا

// addMethod - By Stavros Ioannidis
function addMethod(obj, name, fn) {
  obj[name] = obj[name] || function() {
    // get the cached method with arguments.length arguments
    var method = obj[name].cache[arguments.length];

    // if method exists call it 
    if ( !! method)
      return method.apply(this, arguments);
    else throw new Error("Wrong number of arguments");
  };

  // initialize obj[name].cache
  obj[name].cache = obj[name].cache || {};

  // Check if a method with the same number of arguments exists  
  if ( !! obj[name].cache[fn.length])
    throw new Error("Cannot define multiple '" + name +
      "' methods with the same number of arguments!");

  // cache the method with fn.length arguments
  obj[name].cache[fn.length] = function() {
    return fn.apply(this, arguments);
  };
}

I like @AntouanK's approach. I often find myself offering a function with different numbers o parameters and different types. Sometimes they don't follow a order. I use to map looking the types of parameters:

findUDPServers: function(socketProperties, success, error) {
    var fqnMap = [];

    fqnMap['undefined'] = fqnMap['function'] = function(success, error) {
        var socketProperties = {name:'HELLO_SERVER'};

        this.searchServers(socketProperties, success, error);
    };

    fqnMap['object'] = function(socketProperties, success, error) {
        var _socketProperties = _.merge({name:'HELLO_SERVER'}, socketProperties || {});

        this.searchServers(_socketProperties, success, error);
    };

    fqnMap[typeof arguments[0]].apply(this, arguments);
}

I would like to share a useful example of overloaded-like approach.

function Clear(control)
{
  var o = typeof control !== "undefined" ? control : document.body;
  var children = o.childNodes;
  while (o.childNodes.length > 0)
    o.removeChild(o.firstChild);
}

Usage: Clear(); // Clears all the document

Clear(myDiv); // Clears panel referenced by myDiv


We made over.js to solve this problem is a very elegant way. يمكنك ان تفعل:

var obj = {

  /**
   * Says something in the console.
   *
   * say(msg) - Says something once.
   * say(msg, times) - Says something many times.
   */
  say: Over(
    function(msg$string){
      console.info(msg$string);
    },
    function(msg$string, times$number){
      for (var i = 0; i < times$number; i++) this.say(msg$string);
    }
  )

};

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

شاهد مثالًا على التحميل الزائد للطريقة باستخدام تلك المكتبة:

eutsiv.define('My.Class', {
    constructor: function() {
        this.y = 2;
    },
    x: 3,
    sum: function() {
        return this.x + this.y;
    },
    overloads: {
        value: [
            function() { return this.x + ', ' + this.y },
            function(p1) { this.x = p1; },
            function(p1, p2) { this.x = p1; this.y = p2; }  // will set x and y
        ]
    }
});

var test = new My.Class({ x: 5 });   // create the object
test.value();                        // will return '5, 2'
test.sum();                          // will return 7
test.value(13);                      // will set x to 13
test.value();                        // will return '13, 2'
test.sum();                          // will return 15
test.value(10, 20);                  // will set x to 10 and y to 20
test.value();                        // will return '10, 20'
test.sum();                          // will return 30

نرحب بأي ملاحظات ، وإصلاحات للأخطاء ، والمستندات ، والاختبارات.

https://github.com/eutsiv/eutsiv.js





method-overloading