Programing

자바 스크립트에서 함수를 오버로드하는 방법은 무엇입니까?

lottogame 2020. 8. 28. 07:49
반응형

자바 스크립트에서 함수를 오버로드하는 방법은 무엇입니까?


오버로딩에 대한 고전적인 (비 -js) 접근 방식 :

function myFunc(){
 //code
}

function myFunc(overloaded){
 //other code
}

Javascript는 동일한 이름으로 둘 이상의 함수를 정의 할 수 없습니다. 따라서 다음과 같은 내용이 표시됩니다.

function myFunc(options){
 if(options["overloaded"]){
  //code
 }
}

오버로드가있는 객체를 전달하는 것 외에 자바 스크립트에서 함수 오버로딩에 대한 더 나은 해결 방법이 있습니까?

오버로드를 전달하면 가능한 각 오버로드에 조건문이 필요하기 때문에 함수가 너무 장황해질 수 있습니다. //code이러한 조건문 내부 를 수행하기 위해 함수를 사용하면 범위가있는 까다로운 상황이 발생할 수 있습니다.


Javascript에서 인수 오버로딩에는 여러 측면이 있습니다.

  1. 가변 인수 -다른 인수 세트 (유형 및 수량 모두)를 전달할 수 있으며 함수는 전달 된 인수와 일치하는 방식으로 작동합니다.

  2. 기본 인수 -전달되지 않은 경우 인수의 기본값을 정의 할 수 있습니다.

  3. 명명 된 인수 -인수 순서는 관련이 없으며 함수에 전달할 인수의 이름 만 지정하면됩니다.

다음은 이러한 각 인수 처리 범주에 대한 섹션입니다.

가변 인수

자바 스크립트에는 인수에 대한 유형 검사가 없거나 인수의 수량이 필요 myFunc()하기 때문에 인수의 유형, 존재 또는 수량을 확인하여 전달 된 인수에 적응할 수있는 구현을 하나만 가질 수 있습니다.

jQuery는 항상이 작업을 수행합니다. 인수 중 일부를 선택적으로 만들거나 전달되는 인수에 따라 함수에서 분기 할 수 있습니다.

이러한 유형의 오버로드를 구현할 때 사용할 수있는 몇 가지 다른 기술이 있습니다.

  1. 선언 된 인수 이름 값이인지 확인하여 주어진 인수의 존재를 확인할 수 있습니다 undefined.
  2. 을 사용하여 총 수량 또는 인수를 확인할 수 있습니다 arguments.length.
  3. 주어진 인수의 유형을 확인할 수 있습니다.
  4. 가변 개수의 인수의 경우 arguments의사 배열을 사용하여 arguments[i].

여기 몇 가지 예가 있어요.

jQuery의 obj.data()방법을 살펴 보자 . 다음과 같은 네 가지 사용 형태를 지원합니다.

obj.data("key");
obj.data("key", value);
obj.data();
obj.data(object);

각각은 다른 동작을 트리거하며이 동적 형태의 오버로딩을 사용하지 않으면 4 개의 개별 함수가 필요합니다.

다음은 이러한 모든 옵션을 영어로 구분 한 다음 코드로 모두 결합하는 방법입니다.

// get the data element associated with a particular key value
obj.data("key");

전달 된 첫 번째 인수 .data()가 문자열이고 두 번째 인수가 undefined인 경우 호출자는이 양식을 사용해야합니다.


// set the value associated with a particular key
obj.data("key", value);

두 번째 인수가 정의되지 않은 경우 특정 키의 값을 설정하십시오.


// get all keys/values
obj.data();

인수가 전달되지 않으면 반환 된 객체의 모든 키 / 값을 반환합니다.


// set all keys/values from the passed in object
obj.data(object);

첫 번째 인수의 유형이 일반 객체 인 경우 해당 객체의 모든 키 / 값을 설정합니다.


이 모든 것을 하나의 자바 스크립트 로직 세트로 결합하는 방법은 다음과 같습니다.

 // method declaration for .data()
 data: function(key, value) {
     if (arguments.length === 0) {
         // .data()
         // no args passed, return all keys/values in an object
     } else if (typeof key === "string") {
         // first arg is a string, look at type of second arg
         if (typeof value !== "undefined") {
             // .data("key", value)
             // set the value for a particular key
         } else {
             // .data("key")
             // retrieve a value for a key
         }
     } else if (typeof key === "object") {
         // .data(object)
         // set all key/value pairs from this object
     } else {
         // unsupported arguments passed
     }
 },

이 기술의 핵심은 수락하려는 모든 형식의 인수를 고유하게 식별 할 수 있고 호출자가 어떤 형식을 사용하고 있는지에 대해 혼동이 없도록하는 것입니다. 일반적으로 인수의 순서를 적절하게 지정하고 인수의 유형과 위치에 충분한 고유성이 있는지 확인하여 항상 어떤 형식이 사용되고 있는지 알 수 있어야합니다.

예를 들어, 세 개의 문자열 인수를받는 함수가있는 경우 :

obj.query("firstArg", "secondArg", "thirdArg");

세 번째 인수를 쉽게 선택적으로 만들 수 있고 해당 조건을 쉽게 감지 할 수 있지만 두 번째 인수 만 선택적으로 만들 수는 없습니다. 두 번째 인수를 식별 할 수있는 방법이 없기 때문에 호출자가 전달하는 것을 알 수 없기 때문입니다. 인수는 두 번째 인수를 의미하거나 두 번째 인수가 생략되었으므로 두 번째 인수의 자리에있는 것은 실제로 세 번째 인수입니다.

obj.query("firstArg", "secondArg");
obj.query("firstArg", "thirdArg");

세 개의 인수가 모두 동일한 유형이므로 다른 인수의 차이를 알 수 없으므로 호출자가 의도 한 바를 알 수 없습니다. 이 호출 스타일에서는 세 번째 인수 만 선택 사항이 될 수 있습니다. 두 번째 인수를 생략하려면 null대신 (또는 다른 감지 가능한 값) 으로 전달 해야하며 코드에서 다음을 감지합니다.

obj.query("firstArg", null, "thirdArg");

다음은 선택적 인수의 jQuery 예제입니다. 두 인수 모두 선택 사항이며 전달되지 않으면 기본값을 사용합니다.

clone: function( dataAndEvents, deepDataAndEvents ) {
    dataAndEvents = dataAndEvents == null ? false : dataAndEvents;
    deepDataAndEvents = deepDataAndEvents == null ? dataAndEvents : deepDataAndEvents;

    return this.map( function () {
        return jQuery.clone( this, dataAndEvents, deepDataAndEvents );
    });
},

다음은 인수가 누락 될 수있는 jQuery 예제 또는 네 가지 다른 오버로드를 제공하는 세 가지 유형 중 하나입니다.

html: function( value ) {
    if ( value === undefined ) {
        return this[0] && this[0].nodeType === 1 ?
            this[0].innerHTML.replace(rinlinejQuery, "") :
            null;

    // See if we can take a shortcut and just use innerHTML
    } else if ( typeof value === "string" && !rnoInnerhtml.test( value ) &&
        (jQuery.support.leadingWhitespace || !rleadingWhitespace.test( value )) &&
        !wrapMap[ (rtagName.exec( value ) || ["", ""])[1].toLowerCase() ] ) {

        value = value.replace(rxhtmlTag, "<$1></$2>");

        try {
            for ( var i = 0, l = this.length; i < l; i++ ) {
                // Remove element nodes and prevent memory leaks
                if ( this[i].nodeType === 1 ) {
                    jQuery.cleanData( this[i].getElementsByTagName("*") );
                    this[i].innerHTML = value;
                }
            }

        // If using innerHTML throws an exception, use the fallback method
        } catch(e) {
            this.empty().append( value );
        }

    } else if ( jQuery.isFunction( value ) ) {
        this.each(function(i){
            var self = jQuery( this );

            self.html( value.call(this, i, self.html()) );
        });

    } else {
        this.empty().append( value );
    }

    return this;
},

명명 된 인수

Python과 같은 다른 언어에서는 일부 인수 만 전달하고 전달되는 순서와 관계없이 인수를 만드는 수단으로 명명 된 인수를 전달할 수 있습니다. Javascript는 명명 된 인수의 기능을 직접 지원하지 않습니다. 그 자리에서 일반적으로 사용되는 디자인 패턴은 속성 / 값의 맵을 전달하는 것입니다. 이것은 속성과 값이있는 객체를 전달하거나 ES6 이상에서 실제로 Map 객체 자체를 전달할 수 있습니다.

다음은 간단한 ES5 예입니다.

jQuery $.ajax()는 속성과 값이있는 일반 Javascript 객체 인 단일 매개 변수를 전달하는 사용 형태를 허용합니다. 전달하는 속성은 ajax 호출에 전달되는 인수 / 옵션을 결정합니다. 일부는 필수 일 수 있으며 많은 것은 선택 사항입니다. 객체의 속성이므로 특정 순서가 없습니다. 실제로 해당 객체에 전달할 수있는 30 개 이상의 서로 다른 속성이 있으며 하나 (URL) 만 필요합니다.

예를 들면 다음과 같습니다.

$.ajax({url: "http://www.example.com/somepath", data: myArgs, dataType: "json"}).then(function(result) {
    // process result here
});

$.ajax()그런 다음 구현 에서 들어오는 객체에 전달 된 속성을 조사하여 명명 된 인수로 사용할 수 있습니다. 이는 for (prop in obj)모든 속성을 배열로 가져 오거나 배열 Object.keys(obj)을 반복 하여 수행 할 수 있습니다 .

이 기술은 많은 수의 인수가 있거나 많은 인수가 선택 사항 인 경우 Javascript에서 매우 일반적으로 사용됩니다. 참고 : 이는 최소한의 유효한 인수 집합이 있는지 확인하고 불충분 한 인수가 전달 된 경우 누락 된 부분을 호출자에게 디버그 피드백을 제공하기 위해 구현 함수에 부담을줍니다 (아마도 유용한 오류 메시지와 함께 예외를 발생). .

ES6 환경에서는 위의 전달 된 객체에 대한 기본 속성 / 값을 생성하기 위해 구조 분해를 사용할 수 있습니다. 이에 대해서는 이 참조 문서 에서 자세히 설명 합니다.

다음은 해당 기사의 한 가지 예입니다.

function selectEntries({ start=0, end=-1, step=1 } = {}) {
    ···
};

이렇게하면 함수에 전달 된 객체 start, endstep속성에 대한 기본 속성과 값이 생성 selectEntries()됩니다.

함수 인수의 기본값

ES6에서 Javascript는 인수의 기본값에 대한 기본 제공 언어 지원을 추가합니다.

예를 들면 :

function multiply(a, b = 1) {
  return a*b;
}

multiply(5); // 5

이것이 MDN에서 사용되는 방법에 대한 자세한 설명입니다 .


JavaScript에서 함수 오버로딩은 여러 가지 방법으로 수행 할 수 있습니다. 이들 모두는 모든 프로세스를 수행하거나 하위 기능 / 프로세스에 위임하는 단일 마스터 기능을 포함합니다.

가장 일반적인 간단한 기술 중 하나는 간단한 스위치입니다.

function foo(a, b) {
    switch (arguments.length) {
    case 0:
        //do basic code
        break;
    case 1:
        //do code with `a`
        break;
    case 2:
    default:
        //do code with `a` & `b`
        break;
    }
}

더 우아한 기술은 배열 (또는 모든 인수 개수에 대해 오버로드를 수행하지 않는 경우 객체)을 사용하는 것입니다 .

fooArr = [
    function () {
    },
    function (a) {
    },
    function (a,b) {
    }
];
function foo(a, b) {
    return fooArr[arguments.length](a, b);
}

이전 예제는 그다지 우아하지 않고 누구나 수정할 수 fooArr있으며 누군가에 2 개 이상의 인수를 전달하면 실패 foo하므로 모듈 패턴과 몇 가지 검사를 사용하는 것이 더 나은 형식입니다.

var foo = (function () {
    var fns;
    fns = [
        function () {
        },
        function (a) {
        },
        function (a, b) {
        }
    ];
    function foo(a, b) {
        var fnIndex;
        fnIndex = arguments.length;
        if (fnIndex > foo.length) {
            fnIndex = foo.length;
        }
        return fns[fnIndex].call(this, a, b);
    }
    return foo;
}());

물론 오버로드는 동적 수의 매개 변수를 사용하기를 원할 수 있으므로 fns컬렉션에 개체를 사용할 수 있습니다 .

var foo = (function () {
    var fns;
    fns = {};
    fns[0] = function () {
    };
    fns[1] = function (a) {
    };
    fns[2] = function (a, b) {
    };
    fns.params = function (a, b /*, params */) {
    };
    function foo(a, b) {
        var fnIndex;
        fnIndex = arguments.length;
        if (fnIndex > foo.length) {
            fnIndex = 'params';
        }
        return fns[fnIndex].apply(this, Array.prototype.slice.call(arguments));
    }
    return foo;
}());

내 개인적인 선호 switch는 마스터 기능을 대량으로 만들지 만. 이 기술을 사용하는 일반적인 예는 접근 자 / 변이 자 메서드입니다.

function Foo() {} //constructor
Foo.prototype = {
    bar: function (val) {
        switch (arguments.length) {
        case 0:
            return this._bar;
        case 1:
            this._bar = val;
            return this;
        }
    }
}

엄격한 의미에서 메서드 오버로딩을 수행 할 수 없습니다. java또는 에서 지원되는 방식과 다릅니다 c#.

문제는 JavaScript가 기본적으로 메서드 오버로딩을 지원하지 않는다는 것입니다. 따라서 동일한 이름을 가진 두 개 이상의 함수를보고 / 파싱하면 마지막으로 정의 된 함수를 고려하고 이전 함수를 덮어 씁니다.

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

방법이 있다고 가정 해 봅시다.

function foo(x)
{
} 

자바 스크립트에서 불가능한 오버로딩 메소드 대신 새로운 메소드를 정의 할 수 있습니다.

fooNew(x,y,z)
{
}

다음과 같이 첫 번째 함수를 수정하십시오.

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

이러한 오버로드 된 메서드가 많은 switch경우 단순한 if-else사용을 고려하십시오 .

( 상세 정보 ) 추신 : 위 링크는 이에 대한 추가 정보가있는 개인 블로그로 이동합니다.


인수 번호에 따라 약간 다른 오버로딩 접근 방식을 사용하고 있습니다. 그러나 저는 John Fawcett의 접근 방식도 좋다고 생각합니다. 다음은 John Resig (jQuery의 작성자) 설명을 기반으로 한 코드입니다.

// o = existing object, n = function name, f = function.
    function overload(o, n, f){
        var old = o[n];
        o[n] = function(){
            if(f.length == arguments.length){
                return f.apply(this, arguments);
            }
            else if(typeof o == 'function'){
                return old.apply(this, arguments);
            }
        };
    }

유용성 :

var obj = {};
overload(obj, 'function_name', function(){ /* what we will do if no args passed? */});
overload(obj, 'function_name', function(first){ /* what we will do if 1 arg passed? */});
overload(obj, 'function_name', function(first, second){ /* what we will do if 2 args passed? */});
overload(obj, 'function_name', function(first,second,third){ /* what we will do if 3 args passed? */});
//... etc :)

자바 스크립트에서는 함수를 한 번만 구현하고 매개 변수없이 함수를 호출 할 myFunc()수 있습니다. 그런 다음 옵션이 '정의되지 않음'인지 확인합니다.

function myFunc(options){
 if(typeof options != 'undefined'){
  //code
 }
}

여기에 설명 된이 문제에 대한 우아한 해결책을 개발하려고 노력했습니다 . 여기 에서 데모를 찾을 수 있습니다 . 사용법은 다음과 같습니다.

var out = def({
    'int': function(a) {
        alert('Here is int '+a);
    },

    'float': function(a) {
        alert('Here is float '+a);
    },

    'string': function(a) {
        alert('Here is string '+a);
    },

    'int,string': function(a, b) {
        alert('Here is an int '+a+' and a string '+b);
    },
    'default': function(obj) {
        alert('Here is some other value '+ obj);
    }

});

out('ten');
out(1);
out(2, 'robot');
out(2.5);
out(true);

이를 달성하기 위해 사용 된 방법 :

var def = function(functions, parent) {
 return function() {
    var types = [];
    var args = [];
    eachArg(arguments, function(i, elem) {
        args.push(elem);
        types.push(whatis(elem));
    });
    if(functions.hasOwnProperty(types.join())) {
        return functions[types.join()].apply(parent, args);
    } else {
        if (typeof functions === 'function')
            return functions.apply(parent, args);
        if (functions.hasOwnProperty('default'))
            return functions['default'].apply(parent, args);        
    }
  };
};

var eachArg = function(args, fn) {
 var i = 0;
 while (args.hasOwnProperty(i)) {
    if(fn !== undefined)
        fn(i, args[i]);
    i++;
 }
 return i-1;
};

var whatis = function(val) {

 if(val === undefined)
    return 'undefined';
 if(val === null)
    return 'null';

 var type = typeof val;

 if(type === 'object') {
    if(val.hasOwnProperty('length') && val.hasOwnProperty('push'))
        return 'array';
    if(val.hasOwnProperty('getDate') && val.hasOwnProperty('toLocaleTimeString'))
        return 'date';
    if(val.hasOwnProperty('toExponential'))
        type = 'number';
    if(val.hasOwnProperty('substring') && val.hasOwnProperty('length'))
        return 'string';
 }

 if(type === 'number') {
    if(val.toString().indexOf('.') > 0)
        return 'float';
    else
        return 'int';
 }

 return type;
};

https://github.com/jrf0110/leFunc

var getItems = leFunc({
  "string": function(id){
    // Do something
  },
  "string,object": function(id, options){
    // Do something else
  },
  "string,object,function": function(id, options, callback){
    // Do something different
    callback();
  },
  "object,string,function": function(options, message, callback){
    // Do something ca-raaaaazzzy
    callback();
  }
});

getItems("123abc"); // Calls the first function - "string"
getItems("123abc", {poop: true}); // Calls the second function - "string,object"
getItems("123abc", {butt: true}, function(){}); // Calls the third function - "string,object,function"
getItems({butt: true}, "What what?" function(){}); // Calls the fourth function - "object,string,function"

이것 좀 봐:

http://www.codeproject.com/Articles/688869/Overloading-JavaScript-Functions

기본적으로 클래스에서 오버로드하려는 함수에 번호를 지정한 다음 하나의 함수 호출로 빠르고 쉽게 함수 오버로딩을 추가합니다.


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

}

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


이를 위해 객체에 함수를 추가하는 함수를 만들어야합니다. 그러면 함수에 보내는 인수의 양에 따라 실행됩니다.

<script > 
//Main function to add the methods
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);
  };
}


  var ninjas = {
   values: ["Dean Edwards", "Sam Stephenson", "Alex Russell"]
};

//Here we declare the first function with no arguments passed
  addMethod(ninjas, "find", function(){
    return this.values;
});

//Second function with one argument
  addMethod(ninjas, "find", function(name){
    var ret = [];
    for (var i = 0; i < this.values.length; i++)
      if (this.values[i].indexOf(name) == 0)
        ret.push(this.values[i]);
    return ret;
  });

//Third function with two arguments
  addMethod(ninjas, "find", function(first, last){
    var ret = [];
    for (var i = 0; i < this.values.length; i++)
      if (this.values[i] == (first + " " + last))
        ret.push(this.values[i]);
    return ret;
  });


//Now you can do:
ninjas.find();
ninjas.find("Sam");
ninjas.find("Dean", "Edwards")
</script>

JS에서 오버로딩에 문제가 없습니다. pb 함수를 오버로딩 할 때 깨끗한 코드를 유지하는 방법은 무엇입니까?

두 가지를 기반으로 전달사용 하여 깨끗한 코드를 가질 수 있습니다 .

  1. 인수 수 (함수 호출시).
  2. 인수 유형 (함수를 호출 할 때)

      function myFunc(){
          return window['myFunc_'+arguments.length+Array.from(arguments).map((arg)=>typeof arg).join('_')](...arguments);
       }
    
        /** one argument & this argument is string */
      function myFunc_1_string(){
    
      }
       //------------
       /** one argument & this argument is object */
      function myFunc_1_object(){
    
      }
      //----------
      /** two arguments & those arguments are both string */
      function myFunc_2_string_string(){
    
      }
       //--------
      /** Three arguments & those arguments are : id(number),name(string), callback(function) */
      function myFunc_3_number_string_function(){
                let args=arguments;
                  new Person(args[0],args[1]).onReady(args[3]);
      }
    
       //--- And so on ....   
    

동일한 기능에 대해 인수 그룹을 구별하는 기능을 얻기 위해 상위 함수 내에 하위 함수를 추가하는 것을 좋아합니다.

var doSomething = function() {
    var foo;
    var bar;
};

doSomething.withArgSet1 = function(arg0, arg1) {
    var obj = new doSomething();
    // do something the first way
    return obj;
};

doSomething.withArgSet2 = function(arg2, arg3) {
    var obj = new doSomething();
    // do something the second way
    return obj;
};

달성하려는 것은 함수의 로컬 인수 변수를 사용하는 것이 가장 좋습니다 .

function foo() {
    if (arguments.length === 0) {
        //do something
    }
    if (arguments.length === 1) {
        //do something else
    }
}

foo(); //do something
foo('one'); //do something else

여기에서 작동 방식에 대한 더 나은 설명을 찾을 수 있습니다 .

참고 URL : https://stackoverflow.com/questions/10855908/how-to-overload-functions-in-javascript

반응형