자바 스크립트에서 함수 오버로드-모범 사례


783

Javascript에서 함수 과부하를 가짜로 만드는 가장 좋은 방법은 무엇입니까?

다른 언어와 마찬가지로 Javascript에서 함수를 오버로드 할 수 없다는 것을 알고 있습니다. 두 가지 용도로 기능이 필요 하고 가장 foo(x)좋고 foo(x,y,z)선호되는 방법 인 경우 :

  1. 처음에 다른 이름 사용
  2. 다음과 같은 선택적 인수 사용 y = y || 'default'
  3. 다수의 인수 사용
  4. 인수 유형 확인
  5. 아니면 어떻게?

14
아마도 왜 함수 오버로드가 필요하다고 생각하는지 묻는 것이 도움이 될 것입니다. 나는 그것이 우리를 실제 솔루션에 더 가깝게 할 것이라고 생각합니다.
Breton

1
이것은 닫히지 만 다음을 수행합니다. this.selectBy = {인스턴스 : selectByInstance, // 함수 텍스트 : selectByText, // 함수 값 : selectByValue // 함수};
죄수 제로

내 대답은 런타임 함수 오버로드를 수행하는 방법을 보여 주며 속도 저하가 있으며 Javascript 사양을 피하기 위해 수행하지 않는 것이 좋습니다. 함수 오버로딩은 실제로 컴파일 타임 작업입니다. 나는 학문적 목적으로 만 답을 제공하고 코드에서 그것을 사용할지 여부에 대한 당신의 재량에 맡깁니다.
Keldon Alleyne

2
유용한 경우를 대비하여 유형 기반 메소드 오버로드를 허용하는 경량 js 프레임 워크를 작성했습니다. 분명히 성능과 관련하여 동일한 경고가 적용되지만 지금까지는 내 요구에 잘 맞았지만 여전히 개선의 여지가 많이 있습니다. blog.pebbl.co.uk/2013/01/describejs.html#methodoverloading
Pebbl

답변:


602

매개 변수로 함수 오버로딩을 수행하는 가장 좋은 방법은 인수 길이 또는 유형을 확인하지 않는 것입니다. 유형을 확인하면 코드가 느려지고 Arrays, nulls, Objects 등이 재미 있습니다.

대부분의 개발자가하는 것은 객체의 메서드에 대한 마지막 인수로 객체를 고정하는 것입니다. 이 객체는 무엇이든 담을 수 있습니다.

function foo(a, b, opts) {
  // ...
  if (opts['test']) { } //if test param exists, do something.. 
}


foo(1, 2, {"method":"add"});
foo(3, 4, {"test":"equals", "bar":"tree"});

그런 다음 어쨌든 원하는 방식으로 처리 할 수 ​​있습니다. [스위치, 그렇지 않은 경우 등]


43
이러한 "opts"매개 변수가 사용 / 참조되는 방법을 보여주는 foo ()의 샘플 구현을 제공 할 수 있습니까?
Moe Howard

24
Moe // 이렇게 될 수 있습니다. if(opts['test']) //if test param exists, do something.. if(opts['bar']) //if bar param exists, do something
Deckard

80
이것은 오버로드 기능이 아닙니다. 함수 오버로딩에는 이름은 같지만 매개 변수가 다른 두 개의 개별 함수가 있습니다. 당신이 설명하는 것은 끝에 객체 인수가있는 하나의 함수입니다.
d512

66
@ user1334007 Java / .NET에서와 같이 함수 오버로드가 불가능합니다. 그렇습니다. 이것은 "정확하게"오버로드되지는 않지만 작업을 수행합니다.
epascarello

18
아무도 이미 이것을 묻지 않은 것에 놀랐습니다. 왜 점검 arguments.length이 권장되지 않습니까? 또한, 나는 여기에 와서 대부분의 개발자가하는 일을 읽었습니다 ... , 그러나 이것이 내가 본 유일한 장소라고 확신합니다. 이 방법은 또한 '과부하'를 갖는 구문상의 설탕을 망칩니다!
c24w

169

나는 종종 이것을한다 :

씨#:

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

동등한 JavaScript :

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에서 '정의되지 않음'이며 if 문에서 false로 평가됩니다. 그러나 함수 정의는 p2 및 p3이 선택적이라는 정보를 전달하지 않습니다. 많은 오버로드가 필요한 경우 jQuery는 객체를 매개 변수로 사용하기로 결정했습니다 (예 : jQuery.ajax (options)). 나는 이것이 과부하에 대한 가장 강력하고 명확하게 문서화 가능한 접근 방법이라는 것에 동의하지만, 하나 또는 두 개 이상의 빠른 옵션 매개 변수가 거의 필요하지 않습니다.

편집 : 이안의 제안에 따라 IF 테스트 변경


16
지정되지 않은 매개 변수 undefined는 JS가 아니라 JS에 null있습니다. 가장 좋은 방법은로 설정 undefined하지 않아야하므로 테스트를로 변경하는 한 문제가되지 않습니다 p2 === undefined.
Tamzin Blake

3
false마지막 인수로 전달하면 분기되지 않으므로 "false"끝으로 연결 if(p3)되지 않습니다.
dreamlax

5
간단히 typeof p2 === "undefined"말해 , 귀하 는 아마도 귀하의 모범 사례에서 기대하는 것과 반대 typeof p2 !== "undefined"일 것입니다. 또한 실제로 문자열, 숫자 및 부울을 연결하는 것이 좋습니다.p2 === "number"; p3 === "boolean"
WillFM

8
나는 이것을 좋아한다 : p3 = p3 || '기본값';
Dorian

3
의 의미는 무엇입니까 ===와는 !==? 왜 그냥 사용하지 ==!=?
Ricardo Cruz

76

모든 유형의 매개 변수를 전달할 수 있으므로 JavaScript에는 실제 함수 오버로드가 없습니다. 당신은 많은 방법 함수 내에서 확인해야 인수가 전달 된 것과 그들이 입력합니다.


1
jQuery의 John Resig는 한 번 이것을 시도했지만 그 시도는 순전히 학문적이며 실질적인 이점을 제공하지 않았습니다.
scunliffe

14
John Resig의 기능 과부하는 여기 ejohn.org/blog/javascript-method-overloading
Terrance

@Terrance : Resig의 방법도 좋아합니다. 그것은 매력처럼 작동합니다. 유스 케이스를 검증하기 위해 테스트를 작성하는 방법을 찾아야합니다.
chrisvillanueva

"이 기능은 세상을 변화시키지 않지만 짧고 간결하며 모호한 JavaScript 기능을 사용하므로 내 책에서 이깁니다." :-)
Frerich Raabe

67

정답은 JAVASCRIPT에 과부하가 없다는 것입니다.

기능 내부 점검 / 전환이 과부하가 아닙니다.

오버로딩 개념 : 일부 프로그래밍 언어에서 함수 오버로딩 또는 메서드 오버로딩은 구현이 다른 동일한 이름의 여러 메서드를 만들 수있는 기능입니다. 오버로드 된 함수에 대한 호출은 호출의 컨텍스트에 적합한 해당 기능의 특정 구현을 실행하여 하나의 함수 호출이 컨텍스트에 따라 다른 작업을 수행 할 수 있도록합니다.

예를 들어 doTask () 및 doTask (object O)는 오버로드 된 메서드입니다. 후자를 호출하려면 객체를 매개 변수로 전달해야하지만 전자는 매개 변수를 요구하지 않으며 빈 매개 변수 필드로 호출됩니다. 일반적인 오류는 두 번째 방법에서 객체에 기본값을 할당하는 것인데, 컴파일러는 사용할 두 가지 방법 중 어느 것을 알지 못하기 때문에 모호한 호출 오류가 발생합니다.

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

제안 된 모든 구현은 훌륭하지만 사실은 JavaScript의 기본 구현은 없습니다.


4
마침내 정답! JAVASCRIPT에는 과부하가 없습니다.
Razvan Tudorica

4
OP는 과부하 를 위조 하는 방법을 요청했습니다 .
Ateur Games

내가 전에 말했듯이, 우리는 여기 사람들을 교육하고 있습니다. 우리는 그들이 요구하는 것이 옳은지를 확인하지 않고 그들에게 대답하지 않습니다.
Marco

27

더 잘 접근 할 수있는 두 가지 방법이 있습니다.

  1. 많은 유연성을 유지하려면 사전 (연관 배열)을 전달하십시오.

  2. 객체를 인수로 사용하고 프로토 타입 기반 상속을 사용하여 유연성을 추가하십시오.


1
작성중인 함수가 분명 도움이 될 수있는 값을 열거, 도서관 또는 다른 사람에 의해 사용되는 경우이, 그러나, 나의 초기 생각했다
roberthuttinger

19

다음은 매개 변수 유형을 사용하여 실제 메소드 오버로드를 허용하는 접근법입니다.

Func(new Point());
Func(new Dimension());
Func(new Dimension(), new Point());
Func(0, 0, 0, 0);

편집 (2018) : 2011 년에 작성된 이래로 직접 메서드 호출 속도는 크게 증가했지만 오버로드 된 메서드 속도는 증가하지 않았습니다.

이 방법을 권장하지는 않지만 이러한 유형의 문제를 해결하는 방법에 대해 생각하는 것이 좋습니다.


https://jsperf.com/function-overloading- 다양한 접근 방식의 벤치 마크입니다 . 그것은 그 기능 (계정으로 유형을 복용) 과부하 주위에있을 수 있습니다 보여줍니다 13 배 느린 구글에서 크롬의 V8 기준으로 16.0 (베타) .

객체 (즉 {x: 0, y: 0}) 를 전달할뿐만 아니라 적절한 경우 메소드의 이름을 지정하여 C 접근법을 취할 수도 있습니다. 예를 들어 Vector.AddVector (vector), Vector.AddIntegers (x, y, z, ...) 및 Vector.AddArray (integerArray)입니다. 영감을주는 OpenGL과 같은 C 라이브러리를 볼 수 있습니다.

편집 : 나는 개체를 전달하고 모두를 사용하여 객체에 대한 테스트를위한 벤치 마크를 추가 한 'param' in argarg.hasOwnProperty('param') , 함수 오버로드는 훨씬 더 빨리 (적어도이 벤치 마크에서) 속성에 대한 대상 및 검사를 통과보다.

설계 관점에서 함수 오버로딩은 오버로드 된 매개 변수가 동일한 조치에 해당하는 경우에만 유효하거나 논리적입니다. 따라서 특정 세부 사항에만 관련된 기본 방법이 있어야하며 그렇지 않은 경우 부적절한 디자인 선택을 나타낼 수 있습니다. 따라서 데이터를 개별 객체로 변환하여 함수 오버로드 사용을 해결할 수도 있습니다. 물론 이름을 인쇄하려는 의도가 있다면 정교한 디자인을 만들 필요가 없기 때문에 문제의 범위를 고려해야하지만 프레임 워크와 라이브러리의 디자인에는 그러한 생각이 정당합니다.

내 예제는 Rectangle 구현에서 온 것이므로 Dimension 및 Point에 대한 언급입니다. 아마도 Rectangle이 및 프로토 타입에 GetRectangle()메소드를 추가 한 다음 함수 오버로드 문제가 정렬 될 수 있습니다. 프리미티브는 어떻습니까? 자, 우리는 인자 길이를 가지고 있는데, 이것은 객체에 메소드 가 있기 때문에 이제 유효한 테스트 입니다.DimensionPointGetRectangle()

function Dimension() {}
function Point() {}

var Util = {};

Util.Redirect = function (args, func) {
  'use strict';
  var REDIRECT_ARGUMENT_COUNT = 2;

  if(arguments.length - REDIRECT_ARGUMENT_COUNT !== args.length) {
    return null;
  }

  for(var i = REDIRECT_ARGUMENT_COUNT; i < arguments.length; ++i) {
    var argsIndex = i-REDIRECT_ARGUMENT_COUNT;
    var currentArgument = args[argsIndex];
    var currentType = arguments[i];
    if(typeof(currentType) === 'object') {
      currentType = currentType.constructor;
    }
    if(typeof(currentType) === 'number') {
      currentType = 'number';
    }
    if(typeof(currentType) === 'string' && currentType === '') {
      currentType = 'string';
    }
    if(typeof(currentType) === 'function') {
      if(!(currentArgument instanceof currentType)) {
        return null;
      }
    } else {
      if(typeof(currentArgument) !== currentType) {
        return null;
      }
    } 
  }
  return [func.apply(this, args)];
}

function FuncPoint(point) {}
function FuncDimension(dimension) {}
function FuncDimensionPoint(dimension, point) {}
function FuncXYWidthHeight(x, y, width, height) { }

function Func() {
  Util.Redirect(arguments, FuncPoint, Point);
  Util.Redirect(arguments, FuncDimension, Dimension);
  Util.Redirect(arguments, FuncDimensionPoint, Dimension, Point);
  Util.Redirect(arguments, FuncXYWidthHeight, 0, 0, 0, 0);
}

Func(new Point());
Func(new Dimension());
Func(new Dimension(), new Point());
Func(0, 0, 0, 0);

16

가장 좋은 방법은 실제로 함수와 인수에 따라 다릅니다. 각 상황마다 다른 옵션을 사용하는 것이 좋습니다. 나는 일반적으로 다음 중 하나가 작동 할 때까지 다음 순서로 시도합니다.

  1. y = y ||와 같은 선택적 인수 사용 '기본'. 이것은 가능하다면 편리하지만, 실제로는 작동하지 않을 수도 있습니다. 예를 들어 0 / null / undefined가 유효한 인수 일 때.

  2. 다수의 인수 사용 마지막 옵션과 유사하지만 # 1이 작동하지 않을 때 작동 할 수 있습니다.

  3. 인수 유형 확인 이는 인수 수가 동일한 일부 경우에 작동 할 수 있습니다. 확실하게 유형을 결정할 수 없으면 다른 이름을 사용해야 할 수도 있습니다.

  4. 처음에는 다른 이름을 사용합니다. 다른 옵션이 작동하지 않거나 실용적이지 않거나 다른 관련 기능과의 일관성을 유지하려면이 작업을 수행해야합니다.


14

foo (x)와 foo (x, y, z)를 두 번 사용하는 함수가 필요한 경우 가장 좋은 방법은 무엇입니까?

문제는 JavaScript가 메소드 오버로드를 기본적으로 지원하지 않는다는 것입니다. 따라서 동일한 이름을 가진 둘 이상의 함수를 보거나 구문 분석하면 마지막으로 정의 된 함수를 고려하여 이전 함수를 덮어 씁니다.

내가 대부분의 경우에 적합하다고 생각하는 방법 중 하나는 다음과 같습니다.

방법이 있다고 말해 보자

function foo(x)
{
} 

자바 스크립트에서는 불가능한 오버로드 방법 대신 새로운 방법을 정의 할 수 있습니다

fooNew(x,y,z)
{
}

다음과 같이 첫 번째 기능을 수정하십시오-

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

과부하 된 방법이 많은 switch경우 단순히 사용하는 것보다if-else 명령문 .

( 자세한 내용 )

추신 : 위의 링크는 추가 세부 정보가있는 개인 블로그로 이동합니다.


9

모범 사례에 대해 잘 모르겠지만 여기에 내가하는 방법이 있습니다.

/*
 * Object Constructor
 */
var foo = function(x) {
    this.x = x;
};

/*
 * Object Protoype
 */
foo.prototype = {
    /*
     * f is the name that is going to be used to call the various overloaded versions
     */
    f: function() {

        /*
         * Save 'this' in order to use it inside the overloaded functions
         * because there 'this' has a different meaning.
         */   
        var that = this;  

        /* 
         * Define three overloaded functions
         */
        var f1 = function(arg1) {
            console.log("f1 called with " + arg1);
            return arg1 + that.x;
        }

        var f2 = function(arg1, arg2) {
             console.log("f2 called with " + arg1 + " and " + arg2);
             return arg1 + arg2 + that.x;
        }

        var f3 = function(arg1) {
             console.log("f3 called with [" + arg1[0] + ", " + arg1[1] + "]");
             return arg1[0] + arg1[1];
        }

        /*
         * Use the arguments array-like object to decide which function to execute when calling f(...)
         */
        if (arguments.length === 1 && !Array.isArray(arguments[0])) {
            return f1(arguments[0]);
        } else if (arguments.length === 2) {
            return f2(arguments[0], arguments[1]);
        } else if (arguments.length === 1 && Array.isArray(arguments[0])) {
            return f3(arguments[0]);
        }
    } 
}

/* 
 * Instantiate an object
 */
var obj = new foo("z");

/*
 * Call the overloaded functions using f(...)
 */
console.log(obj.f("x"));         // executes f1, returns "xz"
console.log(obj.f("x", "y"));    // executes f2, returns "xyz"
console.log(obj.f(["x", "y"]));  // executes f3, returns "xy"

2
@ Luis : 희망적으로 도움이되는 의견을 추가했습니다.
chessweb

6

방금 시도해 보았을 것입니다. 인수의 수에 따라 다른 함수에 액세스 할 수 있습니다. 처음 호출 할 때 초기화합니다. 그리고 함수 맵은 클로저에 숨겨져 있습니다.

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

5

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 + "}";

}

다음 은 options 객체를 사용하는 방법에 대한 예입니다


4

자바 스크립트에서 오버로드 기능을 수행 할 방법이 없습니다. 따라서 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!!");
     }
}

행운을 빕니다!


24
제발 좀! switch 문을 사용하십시오!
jedmao

8
또한 스위치를 사용하지 않으려면 typeof를 한 번만 호출해야합니다. var type = typeof param; if (type === 'string') ...
Walter Stabosz

"==="에 대해 +1합니다. if (... == ...)에 대한 switch 문의 다른 장점은 형식이 안전하다는 것입니다.
Nathan Cooper

4

소개

지금까지 너무 많은 답변을 읽으면 누구나 두통이 생길 것입니다. 개념을 알고 자하는 사람은 다음 전제 조건 을 알아야합니다 .

Function overloading Definition, Function Length property,Function argument property

Function overloading가장 간단한 형식은 함수가 전달되는 인수 수에 따라 다른 작업을 수행함을 의미합니다. 특히 TASK1, TASK2 및 TASK3은 아래에 강조 표시되어 있으며 arguments동일한 기능에 전달 된 횟수를 기준으로 수행 되고 있습니다 fooYo.

// if we have a function defined below
function fooYo(){
     // do something here
}
// on invoking fooYo with different number of arguments it should be capable to do different things

fooYo();  // does TASK1
fooYo('sagar'); // does TASK2
fooYo('sagar','munjal'); // does TAKS3

참고 -JS는 내장 기능 오버로드 기능을 제공하지 않습니다.

대안

John E Resig (JS 제작자)는 위의 전제 조건을 사용하여 함수 오버로딩을 구현할 수있는 대안을 지적했습니다.

아래 코드는 if-elseor switch문 을 사용하여 간단하지만 순진한 접근 방식을 사용 합니다.

  • argument-length속성을 평가합니다 .
  • 값이 다르면 다른 기능이 호출됩니다.

var ninja = {
  whatever: function() {
       switch (arguments.length) {
         case 0:
           /* do something */
           break;
         case 1:
           /* do something else */
           break;
         case 2:
           /* do yet something else */
           break;
       //and so on ...
    } 
  }
}

또 다른 기술은 훨씬 더 깨끗하고 역동적입니다. 이 기술의 주요 특징은 addMethod일반적인 기능입니다.

  • 이름은 같지만 기능은 다른addMethod 객체에 다른 함수를 추가하는 데 사용되는 함수 를 정의 합니다 .

  • addMethod함수 아래에는 세 개의 params 객체 이름 object, 함수 이름 name및 호출하려는 함수를 사용할 수 있습니다 fn.

  • 내부 addMethod정의 var oldfunction보호 버블이라는 폐쇄의 도움으로 이전 에 저장된 참조를 저장합니다 .

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세 개의 함수가 추가 됩니다.이 함수 ninja.whatever(x)는 여러 개의 인수 x를 사용하여 호출 할 때 사용할 수 있습니다. 즉 공백 또는 하나 이상의 함수가 함수를 사용하는 동안 정의 된대로 다른 함수를 호출 addMethod합니다.

var ninja = {};
debugger;


addMethod(ninja,'whatever',function(){ console.log("I am the one with ZERO arguments supplied") });
addMethod(ninja,'whatever',function(a){ console.log("I am the one with ONE arguments supplied") });
addMethod(ninja,'whatever',function(a,b){ console.log("I am the one with TWO arguments supplied") });


ninja.whatever();
ninja.whatever(1,2);
ninja.whatever(3);


4

이것에 접근하는 또 다른 방법은 특수 변수 arguments 를 사용 하는 것입니다. 이것은 구현입니다.

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

3

이것 좀 봐. 매우 시원합니다. http://ejohn.org/blog/javascript-method-overloading/ Trick Javascript를 사용하면 다음과 같은 호출을 수행 할 수 있습니다.

var users = new Users();
users.find(); // Finds all
users.find("John"); // Finds users by name
users.find("John", "Resig"); // Finds users by first and last name

안녕하세요 Jaider, 내 답변을 확인하십시오 . 실제 자바 스크립트 메소드 오버로드에 대한 코드가 포함되어 있습니다 . 나는 이야기 Func(new Point())하고 Func(new Rectangle())다른 기능을 실행할 것입니다. 그러나 메서드 오버로딩은 실제로 컴파일 타임 작업이 아니기 때문에 이것이 더러운 해킹임을 지적해야합니다.
Keldon Alleyne

3

자바 스크립트에서 함수 오버로딩 :

함수 오버로딩은 프로그래밍 언어가 다른 구현으로 동일한 이름의 여러 함수를 만들 수있는 기능입니다. 오버로드 된 함수가 호출되면 호출 컨텍스트에 적합한 해당 함수의 특정 구현을 함수에서 실행합니다. 이 컨텍스트는 일반적으로 인수가받는 양이며 컨텍스트에 따라 하나의 함수 호출이 다르게 작동하도록합니다.

Javascript 에는 내장 함수 오버로드 가 없습니다 . 그러나이 동작은 여러 가지 방법으로 에뮬레이션 할 수 있습니다. 다음은 편리한 간단한 것입니다.

function sayHi(a, b) {
  console.log('hi there ' + a);
  if (b) { console.log('and ' + b) } // if the parameter is present, execute the block
}

sayHi('Frank', 'Willem');

얼마나 많은 인수를 얻을지 모르는 시나리오에서는 3 개의 점인 나머지 연산자 를 사용할 수 있습니다 .... 나머지 인수는 배열로 변환합니다. 브라우저 호환성에주의하십시오. 예를 들면 다음과 같습니다.

function foo (a, ...b) {
  console.log(b);
}

foo(1,2,3,4);
foo(1,2);


2

이 게시물에는 이미 다른 솔루션이 많이 포함되어 있으므로 다른 솔루션을 게시한다고 생각했습니다.

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

이 솔루션은 완벽하지는 않지만 어떻게 수행 할 수 있는지 보여주고 싶습니다.


2

John Resig에서 'addMethod'를 사용할 수 있습니다. 이 방법을 사용하면 인수 개수에 따라 메소드를 "오버로드"할 수 있습니다.

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

2

전달 패턴 => 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 ....   

2

100 줄의 JS에서 동적 다형성을 통한 함수 오버로딩

이것은 isFn,,isArr 등의 형태 체크 기능. 아래의 VanillaJS 버전은 모든 외부 종속성을 제거하기 위해 재 작업되었지만 .add()호출 에 사용할 고유 한 유형 검사 기능을 정의해야 합니다.

참고 : 이것은 자체 실행 기능이므로 폐쇄 / 폐쇄 범위를 가질 수 있으므로 window.overload대신에 할당하십시오 function overload() {...}.

window.overload = function () {
    "use strict"

    var a_fnOverloads = [],
        _Object_prototype_toString = Object.prototype.toString
    ;

    function isFn(f) {
        return (_Object_prototype_toString.call(f) === '[object Function]');
    } //# isFn

    function isObj(o) {
        return !!(o && o === Object(o));
    } //# isObj

    function isArr(a) {
        return (_Object_prototype_toString.call(a) === '[object Array]');
    } //# isArr

    function mkArr(a) {
        return Array.prototype.slice.call(a);
    } //# mkArr

    function fnCall(fn, vContext, vArguments) {
        //# <ES5 Support for array-like objects
        //#     See: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/apply#Browser_compatibility
        vArguments = (isArr(vArguments) ? vArguments : mkArr(vArguments));

        if (isFn(fn)) {
            return fn.apply(vContext || this, vArguments);
        }
    } //# fnCall

    //# 
    function registerAlias(fnOverload, fn, sAlias) {
        //# 
        if (sAlias && !fnOverload[sAlias]) {
            fnOverload[sAlias] = fn;
        }
    } //# registerAlias

    //# 
    function overload(vOptions) {
        var oData = (isFn(vOptions) ?
                { default: vOptions } :
                (isObj(vOptions) ?
                    vOptions :
                    {
                        default: function (/*arguments*/) {
                            throw "Overload not found for arguments: [" + mkArr(arguments) + "]";
                        }
                    }
                )
            ),
            fnOverload = function (/*arguments*/) {
                var oEntry, i, j,
                    a = arguments,
                    oArgumentTests = oData[a.length] || []
                ;

                //# Traverse the oArgumentTests for the number of passed a(rguments), defaulting the oEntry at the beginning of each loop
                for (i = 0; i < oArgumentTests.length; i++) {
                    oEntry = oArgumentTests[i];

                    //# Traverse the passed a(rguments), if a .test for the current oArgumentTests fails, reset oEntry and fall from the a(rgument)s loop
                    for (j = 0; j < a.length; j++) {
                        if (!oArgumentTests[i].tests[j](a[j])) {
                            oEntry = undefined;
                            break;
                        }
                    }

                    //# If all of the a(rgument)s passed the .tests we found our oEntry, so break from the oArgumentTests loop
                    if (oEntry) {
                        break;
                    }
                }

                //# If we found our oEntry above, .fn.call its .fn
                if (oEntry) {
                    oEntry.calls++;
                    return fnCall(oEntry.fn, this, a);
                }
                //# Else we were unable to find a matching oArgumentTests oEntry, so .fn.call our .default
                else {
                    return fnCall(oData.default, this, a);
                }
            } //# fnOverload
        ;

        //# 
        fnOverload.add = function (fn, a_vArgumentTests, sAlias) {
            var i,
                bValid = isFn(fn),
                iLen = (isArr(a_vArgumentTests) ? a_vArgumentTests.length : 0)
            ;

            //# 
            if (bValid) {
                //# Traverse the a_vArgumentTests, processinge each to ensure they are functions (or references to )
                for (i = 0; i < iLen; i++) {
                    if (!isFn(a_vArgumentTests[i])) {
                        bValid = _false;
                    }
                }
            }

            //# If the a_vArgumentTests are bValid, set the info into oData under the a_vArgumentTests's iLen
            if (bValid) {
                oData[iLen] = oData[iLen] || [];
                oData[iLen].push({
                    fn: fn,
                    tests: a_vArgumentTests,
                    calls: 0
                });

                //# 
                registerAlias(fnOverload, fn, sAlias);

                return fnOverload;
            }
            //# Else one of the passed arguments was not bValid, so throw the error
            else {
                throw "poly.overload: All tests must be functions or strings referencing `is.*`.";
            }
        }; //# overload*.add

        //# 
        fnOverload.list = function (iArgumentCount) {
            return (arguments.length > 0 ? oData[iArgumentCount] || [] : oData);
        }; //# overload*.list

        //# 
        a_fnOverloads.push(fnOverload);
        registerAlias(fnOverload, oData.default, "default");

        return fnOverload;
    } //# overload

    //# 
    overload.is = function (fnTarget) {
        return (a_fnOverloads.indexOf(fnTarget) > -1);
    } //# overload.is

    return overload;
}();

용법:

호출자는의 반환에 변수를 할당하여 오버로드 된 함수를 정의합니다 overload(). 체인 연결 덕분에 추가 과부하를 직렬로 정의 할 수 있습니다.

var myOverloadedFn = overload(function(){ console.log("default", arguments) })
    .add(function(){ console.log("noArgs", arguments) }, [], "noArgs")
    .add(function(){ console.log("str", arguments) }, [function(s){ return typeof s === 'string' }], "str")
;

overload()서명을 식별 할 수없는 경우 호출 할 "기본"함수 를 정의하는 단일 선택적 인수 입니다. 인수 .add()는 다음과 같습니다.

  1. fn: function과부하를 정의하는 단계;
  2. a_vArgumentTests: Arrayfunction테스트를 정의 s를 실행합니다 arguments. 마다function 은 단일 인수를 허용하며 true인수가 유효한지 여부에 따라 thy를 리턴 합니다.
  3. sAlias(선택 사항) : string과부하 기능에 직접 액세스하기위한 별칭 정의fn ) . 예를 들어 myOverloadedFn.noArgs()인수의 동적 다형성 테스트를 피하면서 해당 함수를 직접 호출합니다.

이 구현은 실제로 기존의 기능 과부하보다 더 많은 것을 허용합니다. a_vArgumentTests 인수로 허용합니다..add() 실제로 사용자 정의 유형을 정의 . 따라서 유형뿐만 아니라 범위, 값 또는 값 모음을 기반으로 인수를 게이트 할 수 있습니다!

145 줄의 코드를 살펴보면 overload() 살펴보면 각 서명이 arguments전달 된 수에 따라 분류되어 있음을 알 수 있습니다. 이것은 우리가 실행중인 테스트의 수를 제한하기 위해 수행됩니다. 또한 통화 횟수를 추적합니다. 일부 추가 코드를 사용하면 오버로드 된 함수의 배열을 다시 정렬하여보다 일반적으로 호출되는 함수를 먼저 테스트하고 성능을 향상시킬 수 있습니다.

이제 몇 가지주의 사항이 있습니다 ...로서 자바 스크립트가 느슨하게 입력 할 때, 당신은 조심해야 할 것 vArgumentTests같은integer 유효성이 검증 될 수 있으므로 .float

JSCompress.com 버전 (1114 바이트, 744 바이트 g 압축) :

window.overload=function(){'use strict';function b(n){return'[object Function]'===m.call(n)}function c(n){return!!(n&&n===Object(n))}function d(n){return'[object Array]'===m.call(n)}function e(n){return Array.prototype.slice.call(n)}function g(n,p,q){if(q=d(q)?q:e(q),b(n))return n.apply(p||this,q)}function h(n,p,q){q&&!n[q]&&(n[q]=p)}function k(n){var p=b(n)?{default:n}:c(n)?n:{default:function(){throw'Overload not found for arguments: ['+e(arguments)+']'}},q=function(){var r,s,t,u=arguments,v=p[u.length]||[];for(s=0;s<v.length;s++){for(r=v[s],t=0;t<u.length;t++)if(!v[s].tests[t](u[t])){r=void 0;break}if(r)break}return r?(r.calls++,g(r.fn,this,u)):g(p.default,this,u)};return q.add=function(r,s,t){var u,v=b(r),w=d(s)?s.length:0;if(v)for(u=0;u<w;u++)b(s[u])||(v=_false);if(v)return p[w]=p[w]||[],p[w].push({fn:r,tests:s,calls:0}),h(q,r,t),q;throw'poly.overload: All tests must be functions or strings referencing `is.*`.'},q.list=function(r){return 0<arguments.length?p[r]||[]:p},l.push(q),h(q,p.default,'default'),q}var l=[],m=Object.prototype.toString;return k.is=function(n){return-1<l.indexOf(n)},k}();

2

이제 폴리 필없이 ECMAScript 2018에서 함수 오버로드를 수행하고 var 길이 / 유형을 확인하는 등 스프레드 구문을 사용할 수 있습니다 .

function foo(var1, var2, opts){
  // set default values for parameters
  const defaultOpts = {
    a: [1,2,3],
    b: true,
    c: 0.3289,
    d: "str",
  }
  // merge default and passed-in parameters
  // defaultOpts must go first!
  const mergedOpts = {...defaultOpts, ...opts};

  // you can now refer to parameters like b as mergedOpts.b,
  // or just assign mergedOpts.b to b
  console.log(mergedOpts.a);
  console.log(mergedOpts.b);
  console.log(mergedOpts.c);  
  console.log(mergedOpts.d);
}
// the parameters you passed in override the default ones
// all JS types are supported: primitives, objects, arrays, functions, etc.
let var1, var2="random var";
foo(var1, var2, {a: [1,2], d: "differentString"});

// parameter values inside foo:
//a: [1,2]
//b: true
//c: 0.3289
//d: "differentString"

스프레드 구문은 무엇입니까?

ECMAScript 제안 (4 단계)의 나머지 / 스프레드 속성은 스프레드 속성을 객체 리터럴에 추가합니다. 제공된 개체에서 자체 열거 가능한 속성을 새 개체로 복사합니다. mdn에 대한 추가 정보

참고 : 개체 리터럴의 스프레드 구문은 Edge 및 IE에서 작동하지 않으며 실험적인 기능입니다. 브라우저 호환성 확인


2

함수 오버로드를 위해 이와 같은 것을 수행 할 수 있습니다.

function addCSS(el, prop, val) {
  return {
    2: function() {
      // when two arguments are set
      // now prop is an oject
      for (var i in prop) {
          el.style[i] = prop[i];
      }
    },
    3: function() {
      // when three arguments are set
      el.style[prop] = val;
    }
    }[arguments.length]();
}
// usage
var el = document.getElementById("demo");
addCSS(el, "color", "blue");
addCSS(el, {
    "backgroundColor": "black",
  "padding": "10px"
});

출처


1

이것은 오래된 질문이지만 다른 항목이 필요하다고 생각합니다 (누군가가 그것을 읽을 지 의심 스럽지만). IIFE (Inmediately Invoked Function Expressions)를 클로저 및 인라인 함수와 함께 사용하여 함수 오버로드를 허용 할 수 있습니다. 다음과 같은 (고려 된) 예를 고려하십시오.

var foo;

// original 'foo' definition
foo = function(a) {
  console.log("a: " + a);
}

// define 'foo' to accept two arguments
foo = (function() {
  // store a reference to the previous definition of 'foo'
  var old = foo;

  // use inline function so that you can refer to it internally
  return function newFoo(a,b) {

    // check that the arguments.length == the number of arguments 
    // defined for 'newFoo'
    if (arguments.length == newFoo.length) {
      console.log("a: " + a);
      console.log("b: " + b);

    // else if 'old' is a function, apply it to the arguments
    } else if (({}).toString.call(old) === '[object Function]') {
      old.apply(null, arguments);
    }
  }
})();

foo(1);
> a: 1
foo(1,2);
> a: 1
> b: 2
foo(1,2,3)
> a: 1

간단히 말해 IIFE를 사용하면 로컬 범위가 만들어 old지고 함수의 초기 정의에 대한 참조를 저장할 개인 변수 를 정의 할 수 있습니다 foo. 이 함수는 인라인 함수 반환 newFoo정확히 두 개의 인수를 전달되는 경우 두 개의 인수의 내용을 기록 a하고 b또는 호출하는 old경우 기능을 arguments.length !== 2. 이 패턴은 몇 가지 다른 기능적 적합성을 갖는 하나의 변수를 부여하기 위해 여러 번 반복 될 수있다.


1

오버로드 된 접근 방식의 유용한 예를 공유하고 싶습니다.

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

사용법 : Clear (); // 모든 문서를 지 웁니다.

클리어 (myDiv); // myDiv에서 참조한 패널을 지 웁니다.


1

JavaScript는 형식화되지 않은 언어이므로 매개 변수 수와 관련하여 메서드 / 함수를 오버로드하는 것이 좋습니다. 따라서 매개 변수가 정의되어 있는지 확인하는 것이 좋습니다.

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

1
형식화되지 않은 것이 "유형 없음"을 의미하지는 않습니다.
hamdiakoguz

1

2017 년 7 월 현재 일반적인 기술은 다음과 같습니다. 함수 내에서 유형 검사를 수행 할 수도 있습니다.

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

1

유스 케이스의 경우 다음과 같이 처리합니다 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;
}

분명히 많은 양의 매개 변수로 작동하도록 조건을 조정하고 그에 따라 조건문을 변경할 수 있습니다.


1

JS에는 실제로 오버로드가 없으며 어쨌든 여러 가지 방법으로 메소드 오버로드를 시뮬레이션 할 수 있습니다.

방법 # 1 : 객체 사용

function test(x,options){
  if("a" in options)doSomething();
  else if("b" in options)doSomethingElse();
}
test("ok",{a:1});
test("ok",{b:"string"});

방법 # 2 : 나머지 (확산) 매개 변수 사용

function test(x,...p){
 if(p[2])console.log("3 params passed"); //or if(typeof p[2]=="string")
else if (p[1])console.log("2 params passed");
else console.log("1 param passed");
}

방법 # 3 : 정의되지 않은 사용

function test(x, y, z){
 if(typeof(z)=="undefined")doSomething();
}

방법 # 4 : 타입 검사

function test(x){
 if(typeof(x)=="string")console.log("a string passed")
 else ...
}

1

하지만 기본 매개 변수가 과부하되지, 그것은 개발자가이 분야에서 직면하고있는 몇 가지 문제를 해결할 수 있습니다. 입력은 주문에 의해 엄격하게 결정되며 클래식 과부하와 같이 원하는대로 다시 주문할 수 없습니다.

function transformer(
    firstNumber = 1,
    secondNumber = new Date().getFullYear(),
    transform = function multiply(firstNumber, secondNumber) {
        return firstNumber * secondNumber;
    }
) {
    return transform(firstNumber, secondNumber);
}

console.info(transformer());
console.info(transformer(8));
console.info(transformer(2, 6));
console.info(transformer(undefined, 65));

function add(firstNumber, secondNumber) {
    return firstNumber + secondNumber;
}
console.info(transformer(undefined, undefined, add));
console.info(transformer(3, undefined, add));

결과 (2020 년) :

2020
16160
12
65
2021
2023

추가 정보 : https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/Default_parameters


0

첫 번째 옵션은 실제로 매우 복잡한 코드 설정에서 나온 것이므로주의를 기울여야합니다. 그래서 제 대답은

  1. 처음에 다른 이름 사용

작지만 필수적인 힌트를 제공하면 이름은 컴퓨터마다 다르지만 사용자에게는 다르게 보일 것입니다. func, func1, func2와 같은 오버로드 된 함수 이름.


난 그냥 다른 이름, 예를 들어 getDeviceInfoByID 및 getDeviceInfoByType ... 사용 과부하를 시도하려고하지만하기로 결정했다
CramerTV
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.