자바 스크립트에서 선택적 매개 변수 처리


127

1, 2 또는 3 개의 매개 변수를 사용할 수있는 정적 자바 스크립트 함수가 있습니다.

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

주어진 매개 변수가 정의되어 있지 않은지 항상 테스트 할 수 있지만 전달 된 것이 매개 변수인지 콜백인지 어떻게 알 수 있습니까?

가장 좋은 방법은 무엇입니까?


전달할 수있는 것의 예 :

1:

getData('offers');

2 :

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

삼:

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

4 :

getData('offers',array,foo);

2
전달할 수있는 예를 보여줄 수 있습니까?
James Black

답변:


163

함수에 몇 개의 인수 가 전달 되었는지 알 수 있으며 두 번째 인수가 함수인지 여부를 확인할 수 있습니다.

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 객체를 사용할 수도 있습니다.

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

관심이 있으시면 John Resig 의이 기사 에서 JavaScript에서 메소드 오버로드를 시뮬레이트하는 기술에 대해 살펴보십시오 .


typeof (parameters) === 'function'이 아닌 Object.prototype.toString.call (parameters) == "[object Function]"을 사용하는 이유는 무엇입니까? 그들 사이에 중요한 차이점이 있습니까? 추신 : 당신이 언급 한 기사는 후자를 사용하는 것 같습니다
Tomer Cagan

@TomerCagan 나는 그것이 선호 (ish)의 문제라고 생각합니다. 질문 의 주제에 대한 좋은 답변 / 의견 있습니다.
Philiiiiiipp

75

어-그것은 당신이 올바른 순서가 아닌 인수로 함수를 호출한다는 것을 의미합니다 ... 권장하지 않을 것입니다.

대신 객체를 함수에 공급하는 것이 좋습니다.

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

혜택:

  • 인수 순서를 설명 할 필요가 없습니다
  • 타입 검사를하지 않아도됩니다
  • 유형 검사가 필요하지 않으므로 기본값을 정의하기가 더 쉽습니다.
  • 덜 두통. 네 번째 인수를 추가했다면 매번 유형 검사를 업데이트해야한다고 가정하십시오.

단점 :

  • 코드 리팩토링 시간

선택의 여지가 없으면 함수를 사용하여 객체가 실제로 함수인지 여부를 감지 할 수 있습니다 (마지막 예 참조).

참고 : 이것은 기능을 감지하는 올바른 방법입니다.

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

isFunction( function(){} )

"일부 ES 버그로 인해 99 %의 시간 동안 작동합니다." 더 설명해 주시겠습니까? 왜 잘못 될 수 있습니까?
jd.

함수를 감지하기 위해 올바른 코드를 추가했습니다. 버그가 여기에 있다고 생각합니다 : bugs.ecmascript.org/ticket/251
meder omuraliev

하나의 물체 만 먹이는 것이 좋습니다. 그렇지 않은 경우 CMS의 방법을 사용하십시오.
meder omuraliev

오 ... 젠장 .. 같은 생각을 올렸어
Arnis Lapsa

1
또 다른 가능한 단점은 지능이 부족하다는 것입니다. 나는 이것을 큰 문제로 생각하지 않지만 주목해야합니다.
Edyn


2

수신 된 매개 변수의 유형을 확인해야합니다. arguments두 번째 매개 변수는 때로는 '매개 변수'일 수 있고 때로는 '콜백'이며 매개 변수의 이름을 지정하는 것이 잘못 될 수 있으므로 배열 을 사용해야합니다 .


2

나는 이것이 꽤 오래된 질문이라는 것을 알고 있지만 최근에 이것을 다루었습니다. 이 솔루션에 대해 어떻게 생각하는지 알려주세요.

인수를 강력하게 입력하고 선택 사항으로 사용할 수있는 유틸리티를 만들었습니다. 기본적으로 함수를 프록시로 래핑합니다. 인수를 건너 뛰면 undefined 입니다. 동일한 유형의 옵션 인수가 서로 옆에 여러 개있는 경우 기발 할 수 있습니다 . (사용자 지정 인수 검사를 수행하고 각 매개 변수의 기본값을 지정하기 위해 유형 대신 함수를 전달하는 옵션이 있습니다.)

구현은 다음과 같습니다.

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

명확성을 위해 다음은 진행중인 작업입니다.

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 ... */

           }
         );
};

내 GitHub 리포지토리에서 adjustArgs 프록시 코드를 볼 수 있습니다.

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

다음은 리포지토리에서 일부 주석이 복사 된 유틸리티 기능입니다.

/*
 ****** 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 를 사용하는 것이 좋습니다 .

이런 식으로 함수를 입력하면됩니다.

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

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

귀하의 예에서 id매개 변수가 문자열이기를 원한다고 생각 했습니다. 이제는을 getData요구하고 String id있으며 선택 사항 Object parameters및을 수락합니다 Function callback. 게시 한 모든 사용 사례가 예상대로 작동합니다.



1

다음과 같이 호출 할 수 있다고 말하고 있습니까? getData (id, parameters); getData (id, 콜백)?

이 경우 분명히 위치에 의존 할 수 없으며 유형을 분석해야합니다 : getType () 그리고 필요한 경우 getTypeName ()

해당 매개 변수가 배열 또는 함수인지 확인하십시오.


0

여기에 typeof ()를 사용하고 싶다고 생각합니다.

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

문제는 함수가 과부하로 인 경우 (당신이 '매개 변수'매개 변수 '매개 변수'가 아니라 '콜백'인지 확인해야합니다), 난 당신이 인수 유형에 대해 귀찮게하지 않는 것이 좋습니다
사용 접근 방식을 . 아이디어는 간단합니다. 리터럴 객체를 사용하여 매개 변수를 결합하십시오.

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

이것은 내가 설명 할 수있는 추측 일 수 있습니다.

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

기능을 무시할 수 있습니까? 작동하지 않습니까?

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

8
아니요, 작동하지 않습니다. 오류는 발생하지 않지만 Javascript는 항상 정의한 최신 기능을 사용합니다.
jd.

6
와. 네가 미쳤다고 생각 했어 방금 테스트했습니다. 네 말이 맞아 세상은 저를 위해 조금 바뀌 었습니다. 프로덕션에 이와 같은 것이 없는지 확인하기 위해 오늘 살펴볼 JavaScript가 많이 있다고 생각합니다. 의견 주셔서 감사합니다. 나는 당신에게 +1을 주었다.
J.Hendrix
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.