Javascript'te isteğe bağlı parametreleri kullanma


127

1, 2 veya 3 parametre alabilen statik bir javascript işlevim var:

function getData(id, parameters, callback) //parameters (associative array) and callback (function) are optional

Belirli bir parametrenin tanımsız olup olmadığını her zaman test edebileceğimi biliyorum, ancak geçirilenin parametre mi yoksa geri arama mı olduğunu nasıl bilebilirim?

Bunu yapmanın en iyi yolu nedir?


Nelerin aktarılabileceğine dair örnekler:

1:

getData('offers');

2:

var array = new Array();
array['type']='lalal';
getData('offers',array);

3:

var foo = function (){...}
getData('offers',foo);

4:

getData('offers',array,foo);

2
Nelerin aktarılabileceğine dair bir örnek gösterebilir misiniz?
James Black

Yanıtlar:


163

Fonksiyonunuza kaç argüman geçirildiğini bilebilir ve ikinci argümanınızın bir fonksiyon olup olmadığını kontrol edebilirsiniz:

function getData (id, parameters, callback) {
  if (arguments.length == 2) { // if only two arguments were supplied
    if (Object.prototype.toString.call(parameters) == "[object Function]") {
      callback = parameters; 
    }
  }
  //...
}

Arguments nesnesini şu şekilde de kullanabilirsiniz:

function getData (/*id, parameters, callback*/) {
  var id = arguments[0], parameters, callback;

  if (arguments.length == 2) { // only two arguments supplied
    if (Object.prototype.toString.call(arguments[1]) == "[object Function]") {
      callback = arguments[1]; // if is a function, set as 'callback'
    } else {
      parameters = arguments[1]; // if not a function, set as 'parameters'
    }
  } else if (arguments.length == 3) { // three arguments supplied
      parameters = arguments[1];
      callback = arguments[2];
  }
  //...
}

İlgileniyorsanız, JavaScript'te aşırı yöntem yüklemesini simüle etmek için bir teknik hakkında John Resig tarafından yazılan bu makaleye bir göz atın .


Neden Object.prototype.toString.call (parametreler) == "[nesne Fonksiyonu]" kullanıp typeof (parametreler) === 'function' kullanmıyorsunuz? Aralarında önemli bir fark var mı? Not
Bahsettiğiniz

@TomerCagan Bunun bir tercih meselesi olduğunu düşünüyorum (ish). Bu sorunun altındaki konuyla ilgili bazı iyi cevaplar / yorumlar var .
Philiiiiiipp

75

Er - bu, işlevinizi uygun sırada olmayan argümanlarla çağırdığınızı gösterir ... ki bunu tavsiye etmem.

Bunun yerine işlevinize şu şekilde bir nesne beslemenizi öneririm:

function getData( props ) {
    props = props || {};
    props.params = props.params || {};
    props.id = props.id || 1;
    props.callback = props.callback || function(){};
    alert( props.callback )
};

getData( {
    id: 3,
    callback: function(){ alert('hi'); }
} );

Yararları:

  • argüman sırasını hesaba katmanıza gerek yok
  • tip kontrolü yapmak zorunda değilsin
  • varsayılan değerleri tanımlamak daha kolaydır çünkü tür denetimi gerekmez
  • daha az baş ağrısı. Dördüncü bir bağımsız değişken eklediyseniz, her seferinde tür kontrolünüzü güncellemeniz gerektiğini ve ya dördüncü veya ardışık da işlevlerse ne olur?

Dezavantajları:

  • kodu yeniden düzenleme zamanı

Seçeneğiniz yoksa, bir nesnenin gerçekten bir işlev olup olmadığını algılamak için bir işlev kullanabilirsiniz (son örneğe bakın).

Not: Bu, bir işlevi algılamanın uygun yoludur:

function isFunction(obj) {
    return Object.prototype.toString.call(obj) === "[object Function]";
}

isFunction( function(){} )

"Bu, bazı ES hataları nedeniyle% 99 oranında çalışır." Biraz daha fazla açıklayabilir misin? Neden ters gidebilir?
jd.

Bir işlevi algılamak için doğru kodu ekledim. Hatanın burada olduğuna inanıyorum: bugs.ecmascript.org/ticket/251
meder omuraliev

Sadece bir nesneyi beslemenizi şiddetle tavsiye ederim. Değilse, CMS'nin yöntemini kullanın.
meder omuraliev

Oh ... kahretsin ... Sadece aynı fikri gönderdim.
Arnis Lapsa

1
Bir diğer olası dezavantaj, zeka eksikliğidir. Bunu büyük bir sorun olarak görmüyorum, ancak not edilmelidir.
Edyn


2

Alınan parametrelerin türünü kontrol etmelisiniz. argumentsİkinci parametre bazen 'parametreler' ve bazen 'geri arama' olabileceğinden ve parametrelerin adlandırılması yanıltıcı olabileceğinden belki de dizi kullanmalısınız .


2

Bunun oldukça eski bir soru olduğunu biliyorum ama son zamanlarda bununla ilgilenmiştim. Bu çözüm hakkında ne düşündüğünüzü bana bildirin.

Argümanları güçlü bir şekilde yazmama ve isteğe bağlı olmalarına izin veren bir yardımcı program yarattım. Temel olarak işlevinizi bir proxy'ye sararsınız. Bir argümanı atlarsanız, tanımsızdır . Aynı türde hemen yan yana olan birden fazla isteğe bağlı argümanınız varsa, garip gelebilir . (Özel bağımsız değişken kontrolleri yapmak için türler yerine işlevleri iletmenin yanı sıra her parametre için varsayılan değerleri belirtme seçenekleri vardır.)

Uygulama şu şekilde görünüyor:

function displayOverlay(/*message, timeout, callback*/) {
  return arrangeArgs(arguments, String, Number, Function, 
    function(message, timeout, callback) {
      /* ... your code ... */
    });
};

Netlik için, neler olup bittiğini burada bulabilirsiniz:

function displayOverlay(/*message, timeout, callback*/) {
  //arrangeArgs is the proxy
  return arrangeArgs(
           //first pass in the original arguments
           arguments, 
           //then pass in the type for each argument
           String,  Number,  Function, 
           //lastly, pass in your function and the proxy will do the rest!
           function(message, timeout, callback) {

             //debug output of each argument to verify it's working
             console.log("message", message, "timeout", timeout, "callback", callback);

             /* ... your code ... */

           }
         );
};

DüzenArgs proxy kodunu GitHub depomda burada görüntüleyebilirsiniz:

https://github.com/joelvh/Sysmo.js/blob/master/sysmo.js

Depodan kopyalanan bazı yorumlarla birlikte yardımcı program işlevi:

/*
 ****** Overview ******
 * 
 * Strongly type a function's arguments to allow for any arguments to be optional.
 * 
 * Other resources:
 * http://ejohn.org/blog/javascript-method-overloading/
 * 
 ****** Example implementation ******
 * 
 * //all args are optional... will display overlay with default settings
 * var displayOverlay = function() {
 *   return Sysmo.optionalArgs(arguments, 
 *            String, [Number, false, 0], Function, 
 *            function(message, timeout, callback) {
 *              var overlay = new Overlay(message);
 *              overlay.timeout = timeout;
 *              overlay.display({onDisplayed: callback});
 *            });
 * }
 * 
 ****** Example function call ******
 * 
 * //the window.alert() function is the callback, message and timeout are not defined.
 * displayOverlay(alert);
 * 
 * //displays the overlay after 500 miliseconds, then alerts... message is not defined.
 * displayOverlay(500, alert);
 * 
 ****** Setup ******
 * 
 * arguments = the original arguments to the function defined in your javascript API.
 * config = describe the argument type
 *  - Class - specify the type (e.g. String, Number, Function, Array) 
 *  - [Class/function, boolean, default] - pass an array where the first value is a class or a function...
 *                                         The "boolean" indicates if the first value should be treated as a function.
 *                                         The "default" is an optional default value to use instead of undefined.
 * 
 */
arrangeArgs: function (/* arguments, config1 [, config2] , callback */) {
  //config format: [String, false, ''], [Number, false, 0], [Function, false, function(){}]
  //config doesn't need a default value.
  //config can also be classes instead of an array if not required and no default value.

  var configs = Sysmo.makeArray(arguments),
      values = Sysmo.makeArray(configs.shift()),
      callback = configs.pop(),
      args = [],
      done = function() {
        //add the proper number of arguments before adding remaining values
        if (!args.length) {
          args = Array(configs.length);
        }
        //fire callback with args and remaining values concatenated
        return callback.apply(null, args.concat(values));
      };

  //if there are not values to process, just fire callback
  if (!values.length) {
    return done();
  }

  //loop through configs to create more easily readable objects
  for (var i = 0; i < configs.length; i++) {

    var config = configs[i];

    //make sure there's a value
    if (values.length) {

      //type or validator function
      var fn = config[0] || config,
          //if config[1] is true, use fn as validator, 
          //otherwise create a validator from a closure to preserve fn for later use
          validate = (config[1]) ? fn : function(value) {
            return value.constructor === fn;
          };

      //see if arg value matches config
      if (validate(values[0])) {
        args.push(values.shift());
        continue;
      }
    }

    //add a default value if there is no value in the original args
    //or if the type didn't match
    args.push(config[2]);
  }

  return done();
}

2

ArgueJS kullanmanızı tavsiye ederim .

İşlevinizi şu şekilde yazabilirsiniz:

function getData(){
  arguments = __({id: String, parameters: [Object], callback: [Function]})

  // and now access your arguments by arguments.id,
  //          arguments.parameters and arguments.callback
}

Örneklerinize bakarak idparametrenizin bir dizge olmasını istediğinizi düşündüm , değil mi? Şimdi, getDataa gerektiriyor String idve isteğe bağlı Object parametersve kabul ediyor Function callback. Gönderdiğiniz tüm kullanım senaryoları beklendiği gibi çalışacaktır.



1

Bunun gibi çağrılara sahip olabileceğinizi mi söylüyorsunuz: getData (id, parametreler); getData (id, geri arama)?

Bu durumda açıkça konuma güvenemezsiniz ve türü analiz etmeye güvenmeniz gerekir: getType () ve sonra gerekirse getTypeName ()

Söz konusu parametrenin bir dizi mi yoksa işlev mi olduğunu kontrol edin.


0

Sanırım burada typeof () kullanmak istiyorsunuz:

function f(id, parameters, callback) {
  console.log(typeof(parameters)+" "+typeof(callback));
}

f("hi", {"a":"boo"}, f); //prints "object function"
f("hi", f, {"a":"boo"}); //prints "function object"

0

Eğer sorununuz sadece fonksiyon aşırı yüklemesi ile ilgiliyse ('parametreler' parametresinin 'geri arama' değil 'parametreler' olup olmadığını kontrol etmeniz gerekiyor), argüman türü hakkında endişelenmemenizi ve bu yaklaşımı
kullanmanızı tavsiye ederim . Fikir basit - parametrelerinizi birleştirmek için değişmez nesneler kullanın:

function getData(id, opt){
    var data = voodooMagic(id, opt.parameters);
    if (opt.callback!=undefined)
      opt.callback.call(data);
    return data;         
}

getData(5, {parameters: "1,2,3", callback: 
    function(){for (i=0;i<=1;i--)alert("FAIL!");}
});

0

Sanırım bu kendi kendini açıklayan bir örnek olabilir:

function clickOn(elem /*bubble, cancelable*/) {
    var bubble =     (arguments.length > 1)  ? arguments[1] : true;
    var cancelable = (arguments.length == 3) ? arguments[2] : true;

    var cle = document.createEvent("MouseEvent");
    cle.initEvent("click", bubble, cancelable);
    elem.dispatchEvent(cle);
}

-5

İşlevi geçersiz kılabilir misin? Bu işe yaramayacak mı:

function doSomething(id){}
function doSomething(id,parameters){}
function doSomething(id,parameters,callback){}

8
Hayır, bu işe yaramayacak. Herhangi bir hata almazsınız ancak Javascript her zaman tanımladığınız en son işlevi kullanır.
jd.

6
Vay. Deli olduğunu sanıyordum. Daha yeni test ettim. Haklısın. Dünyam benim için biraz değişti. Üretimde buna benzer bir şey olmadığından emin olmak için bugün bakmam gereken çok fazla JavaScript olduğunu düşünüyorum. Yorum için teşekkürler. Sana +1 verdim.
J.Hendrix
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.