Nice programing

자바 스크립트의 함수 오버로딩-모범 사례

nicepro 2020. 9. 29. 18:44
반응형

자바 스크립트의 함수 오버로딩-모범 사례


Javascript에서 함수 오버로딩을 가짜로 만드는 가장 좋은 방법은 무엇입니까?

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

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

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

대부분의 개발자는 자신의 메서드에 대한 마지막 인수로 객체를 지정합니다. 이 물체는 무엇이든 담을 수 있습니다.

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

그런 다음 방법에서 원하는대로 처리 할 수 ​​있습니다. [Switch, if-else 등]


나는 종종 이것을한다 :

씨#:

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

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


모든 유형의 매개 변수를 얼마든지 전달할 수 있으므로 JavaScript에는 실제 함수 오버로딩이 없습니다. 함수 내부에서 얼마나 많은 인수 가 전달되었고 어떤 유형인지 확인해야합니다.


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

기능 내부의 확인 / 전환이 OVERLOADING이 아닙니다.

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

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

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

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


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

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

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


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

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

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

내가 권장하는 접근 방식은 아니지만 이러한 유형의 문제를 해결하는 방법에 대해 생각하는 것은 가치있는 사고 연습입니다.


다음은 다양한 접근 방식의 벤치 마크입니다-https: //jsperf.com/function-overloading . 함수 오버로딩 (유형 고려) 16.0 (베타) 현재 Google 크롬의 V8 에서 13 배 느릴 수 있음을 보여줍니다 .

객체 (예 :)를 전달할뿐만 아니라 {x: 0, y: 0}적절한 경우 C 접근 방식을 사용하여 그에 따라 메서드 이름을 지정할 수도 있습니다. 예를 들어 Vector.AddVector (vector), Vector.AddIntegers (x, y, z, ...) 및 Vector.AddArray (integerArray)입니다. 명명 영감을 얻기 위해 OpenGL과 같은 C 라이브러리를 살펴볼 수 있습니다.

편집 : 나는 개체를 전달하고 모두를 사용하여 객체에 대한 테스트를위한 벤치 마크를 추가 한 'param' in arg하고 arg.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);

가장 좋은 방법은 실제로 함수와 인수에 따라 다릅니다. 각 옵션은 다른 상황에서 좋은 아이디어입니다. 일반적으로 그중 하나가 작동 할 때까지 다음 순서로 시도합니다.

  1. y = y와 같은 선택적 인수 사용 || '기본'. 할 수 있으면 편리하지만, 예를 들어 0 / null / undefined가 유효한 인수가 될 때와 같이 항상 실제로 작동하는 것은 아닙니다.

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

  3. 인수 유형 확인. 이는 인수 수가 동일한 경우에 작동 할 수 있습니다. 유형을 안정적으로 결정할 수없는 경우 다른 이름을 사용해야 할 수 있습니다.

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


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사용을 고려하십시오 .

( 자세한 내용 )

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


모범 사례에 대해 잘 모르겠지만 방법은 다음과 같습니다.

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

나는 이것을 시도했다. 아마도 그것은 당신의 필요에 맞을 것이다. 인수의 수에 따라 다른 함수에 액세스 할 수 있습니다. 처음 호출 할 때 초기화합니다. 그리고 함수 맵은 클로저에 숨겨져 있습니다.

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

}

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


자바 스크립트에서 오버로딩을 할 수있는 방법은 없습니다. 그래서 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!!");
     }
}

행운을 빕니다!


소개

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

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-else또는 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함수 아래에는 세 개의 매개 변수 객체 이름 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)xaddMethod

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


이에 접근하는 또 다른 방법은 특수 변수 인 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;
}

이것 좀 봐. 매우 멋지다. http://ejohn.org/blog/javascript-method-overloading/ 다음과 같은 호출을 할 수 있도록 자바 스크립트를 속이십시오.

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

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

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

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


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

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

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는 다음과 같이 integerA와 유효성을 검사 할 수있는 float

JSCompress.com 버전 (1114 바이트, 744 바이트 g-zip) :

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

이제 폴리 필없이 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 단계)에 대한 Rest / Spread 속성은 개체 리터럴에 스프레드 속성을 추가합니다. 제공된 개체에서 새 개체로 열거 가능한 속성을 복사합니다. MDN에 대한 추가 정보

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


함수 오버로딩을 위해 이와 같은 작업을 수행 할 수 있습니다.

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. 처음에 다른 이름 사용

약간의 필수 힌트를 사용하면 이름이 컴퓨터에서는 다르게 보일 수 있지만 사용자에게는 달라야합니다. func, func1, func2와 같이 오버로드 된 함수의 이름을 지정합니다.


이것은 오래된 질문이지만 다른 항목이 필요하다고 생각하는 질문입니다 (누군가가 그것을 읽을 것 같지는 않지만). IIFE (Immediately 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. 이 패턴을 여러 번 반복하여 하나의 변수에 여러 가지 기능적 결함을 부여 할 수 있습니다.


JavaScript는 형식이 지정되지 않은 언어이며 매개 변수 수와 관련하여 메서드 / 함수를 오버로드하는 것이 합리적이라고 생각합니다. 따라서 매개 변수가 정의되었는지 확인하는 것이 좋습니다.

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

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

귀하의 유스 케이스의 경우 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 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');

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

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

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


이 문제를 해결하기 위해 over.js만드는 것은 매우 우아한 방법입니다. 넌 할 수있어:

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

};

그래서 저는 자바 스크립트 닌자의 비밀에서 찾은 이런 방식을 정말 좋아했습니다.

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를 사용하여 개체에 오버로드 된 함수를 추가합니다. 이 코드의 주요 혼란은 fn.length == arguments.length 사용이었습니다. fn.length는 예상 매개 변수의 수이고 arguments.length는 실제로 호출되는 매개 변수의 수이기 때문에 작동합니다. 함수. 익명 함수에 인수가없는 이유는 자바 스크립트에서 여러 인수를 전달할 수 있고 언어가 용서하기 때문입니다.

어디에서나 사용할 수 있기 때문에이 기능이 마음에 들었습니다.이 함수를 만들고 원하는 코드베이스에서 메서드를 사용하기 만하면됩니다.

또한 복잡한 코드를 작성하기 시작하면 읽기 어려워지는 엄청나게 큰 if / switch 문을 피할 수 있습니다.

단점으로는 코드가 처음에는 약간 모호한 것 같지만 다른 사람은 확실하지 않습니까?


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

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 (); // 모든 문서를 지 웁니다.

Clear (myDiv); // myDiv에서 참조하는 패널을 지 웁니다.

참고 URL : https://stackoverflow.com/questions/456177/function-overloading-in-javascript-best-practices

반응형