Javascript에서 함수 과부하를 가짜로 만드는 가장 좋은 방법은 무엇입니까?
다른 언어와 마찬가지로 Javascript에서 함수를 오버로드 할 수 없다는 것을 알고 있습니다. 두 가지 용도로 기능이 필요 하고 가장 foo(x)
좋고 foo(x,y,z)
선호되는 방법 인 경우 :
- 처음에 다른 이름 사용
- 다음과 같은 선택적 인수 사용
y = y || 'default'
- 다수의 인수 사용
- 인수 유형 확인
- 아니면 어떻게?
Javascript에서 함수 과부하를 가짜로 만드는 가장 좋은 방법은 무엇입니까?
다른 언어와 마찬가지로 Javascript에서 함수를 오버로드 할 수 없다는 것을 알고 있습니다. 두 가지 용도로 기능이 필요 하고 가장 foo(x)
좋고 foo(x,y,z)
선호되는 방법 인 경우 :
y = y || 'default'
답변:
매개 변수로 함수 오버로딩을 수행하는 가장 좋은 방법은 인수 길이 또는 유형을 확인하지 않는 것입니다. 유형을 확인하면 코드가 느려지고 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"});
그런 다음 어쨌든 원하는 방식으로 처리 할 수 있습니다. [스위치, 그렇지 않은 경우 등]
if(opts['test']) //if test param exists, do something.. if(opts['bar']) //if bar param exists, do something
arguments.length
이 권장되지 않습니까? 또한, 나는 여기에 와서 대부분의 개발자가하는 일을 읽었습니다 ... , 그러나 이것이 내가 본 유일한 장소라고 확신합니다. 이 방법은 또한 '과부하'를 갖는 구문상의 설탕을 망칩니다!
나는 종종 이것을한다 :
씨#:
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 테스트 변경
undefined
는 JS가 아니라 JS에 null
있습니다. 가장 좋은 방법은로 설정 undefined
하지 않아야하므로 테스트를로 변경하는 한 문제가되지 않습니다 p2 === undefined
.
false
마지막 인수로 전달하면 분기되지 않으므로 "false"
끝으로 연결 if(p3)
되지 않습니다.
typeof p2 === "undefined"
말해 , 귀하 는 아마도 귀하의 모범 사례에서 기대하는 것과 반대 typeof p2 !== "undefined"
일 것입니다. 또한 실제로 문자열, 숫자 및 부울을 연결하는 것이 좋습니다.p2 === "number"; p3 === "boolean"
===
와는 !==
? 왜 그냥 사용하지 ==
와 !=
?
모든 유형의 매개 변수를 전달할 수 있으므로 JavaScript에는 실제 함수 오버로드가 없습니다. 당신은 많은 방법 함수 내에서 확인해야 인수가 전달 된 것과 그들이 입력합니다.
정답은 JAVASCRIPT에 과부하가 없다는 것입니다.
기능 내부 점검 / 전환이 과부하가 아닙니다.
오버로딩 개념 : 일부 프로그래밍 언어에서 함수 오버로딩 또는 메서드 오버로딩은 구현이 다른 동일한 이름의 여러 메서드를 만들 수있는 기능입니다. 오버로드 된 함수에 대한 호출은 호출의 컨텍스트에 적합한 해당 기능의 특정 구현을 실행하여 하나의 함수 호출이 컨텍스트에 따라 다른 작업을 수행 할 수 있도록합니다.
예를 들어 doTask () 및 doTask (object O)는 오버로드 된 메서드입니다. 후자를 호출하려면 객체를 매개 변수로 전달해야하지만 전자는 매개 변수를 요구하지 않으며 빈 매개 변수 필드로 호출됩니다. 일반적인 오류는 두 번째 방법에서 객체에 기본값을 할당하는 것인데, 컴파일러는 사용할 두 가지 방법 중 어느 것을 알지 못하기 때문에 모호한 호출 오류가 발생합니다.
https://en.wikipedia.org/wiki/Function_overloading
제안 된 모든 구현은 훌륭하지만 사실은 JavaScript의 기본 구현은 없습니다.
더 잘 접근 할 수있는 두 가지 방법이 있습니다.
많은 유연성을 유지하려면 사전 (연관 배열)을 전달하십시오.
객체를 인수로 사용하고 프로토 타입 기반 상속을 사용하여 유연성을 추가하십시오.
다음은 매개 변수 유형을 사용하여 실제 메소드 오버로드를 허용하는 접근법입니다.
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 arg
과arg.hasOwnProperty('param')
, 함수 오버로드는 훨씬 더 빨리 (적어도이 벤치 마크에서) 속성에 대한 대상 및 검사를 통과보다.
설계 관점에서 함수 오버로딩은 오버로드 된 매개 변수가 동일한 조치에 해당하는 경우에만 유효하거나 논리적입니다. 따라서 특정 세부 사항에만 관련된 기본 방법이 있어야하며 그렇지 않은 경우 부적절한 디자인 선택을 나타낼 수 있습니다. 따라서 데이터를 개별 객체로 변환하여 함수 오버로드 사용을 해결할 수도 있습니다. 물론 이름을 인쇄하려는 의도가 있다면 정교한 디자인을 만들 필요가 없기 때문에 문제의 범위를 고려해야하지만 프레임 워크와 라이브러리의 디자인에는 그러한 생각이 정당합니다.
내 예제는 Rectangle 구현에서 온 것이므로 Dimension 및 Point에 대한 언급입니다. 아마도 Rectangle이 및 프로토 타입에 GetRectangle()
메소드를 추가 한 다음 함수 오버로드 문제가 정렬 될 수 있습니다. 프리미티브는 어떻습니까? 자, 우리는 인자 길이를 가지고 있는데, 이것은 객체에 메소드 가 있기 때문에 이제 유효한 테스트 입니다.Dimension
Point
GetRectangle()
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);
가장 좋은 방법은 실제로 함수와 인수에 따라 다릅니다. 각 상황마다 다른 옵션을 사용하는 것이 좋습니다. 나는 일반적으로 다음 중 하나가 작동 할 때까지 다음 순서로 시도합니다.
y = y ||와 같은 선택적 인수 사용 '기본'. 이것은 가능하다면 편리하지만, 실제로는 작동하지 않을 수도 있습니다. 예를 들어 0 / null / undefined가 유효한 인수 일 때.
다수의 인수 사용 마지막 옵션과 유사하지만 # 1이 작동하지 않을 때 작동 할 수 있습니다.
인수 유형 확인 이는 인수 수가 동일한 일부 경우에 작동 할 수 있습니다. 확실하게 유형을 결정할 수 없으면 다른 이름을 사용해야 할 수도 있습니다.
처음에는 다른 이름을 사용합니다. 다른 옵션이 작동하지 않거나 실용적이지 않거나 다른 관련 기능과의 일관성을 유지하려면이 작업을 수행해야합니다.
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 + "}";
}
다음 은 options 객체를 사용하는 방법에 대한 예입니다
자바 스크립트에서 오버로드 기능을 수행 할 방법이 없습니다. 따라서 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!!");
}
}
행운을 빕니다!
var type = typeof param; if (type === 'string') ...
소개
지금까지 너무 많은 답변을 읽으면 누구나 두통이 생길 것입니다. 개념을 알고 자하는 사람은 다음 전제 조건 을 알아야합니다 .
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
or 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 old
는 function
보호 버블이라는 폐쇄의 도움으로 이전 에 저장된 참조를 저장합니다 .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);
이것에 접근하는 또 다른 방법은 특수 변수 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/ 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
Func(new Point())
하고 Func(new Rectangle())
다른 기능을 실행할 것입니다. 그러나 메서드 오버로딩은 실제로 컴파일 타임 작업이 아니기 때문에 이것이 더러운 해킹임을 지적해야합니다.
함수 오버로딩은 프로그래밍 언어가 다른 구현으로 동일한 이름의 여러 함수를 만들 수있는 기능입니다. 오버로드 된 함수가 호출되면 호출 컨텍스트에 적합한 해당 함수의 특정 구현을 함수에서 실행합니다. 이 컨텍스트는 일반적으로 인수가받는 양이며 컨텍스트에 따라 하나의 함수 호출이 다르게 작동하도록합니다.
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);
이 게시물에는 이미 다른 솔루션이 많이 포함되어 있으므로 다른 솔루션을 게시한다고 생각했습니다.
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);
};
}
세 번째 및 네 번째 지점에서 이름이 작성된 다른 함수로 전달하십시오.
- 다수의 인수 사용
- 인수 유형 확인
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 ....
이것은 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()
는 다음과 같습니다.
fn
: function
과부하를 정의하는 단계;a_vArgumentTests
: Array
의 function
테스트를 정의 s를 실행합니다 arguments
. 마다function
은 단일 인수를 허용하며 true
인수가 유효한지 여부에 따라 thy를 리턴 합니다.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}();
이제 폴리 필없이 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에서 작동하지 않으며 실험적인 기능입니다. 브라우저 호환성 확인
함수 오버로드를 위해 이와 같은 것을 수행 할 수 있습니다.
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"
});
이것은 오래된 질문이지만 다른 항목이 필요하다고 생각합니다 (누군가가 그것을 읽을 지 의심 스럽지만). 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
. 이 패턴은 몇 가지 다른 기능적 적합성을 갖는 하나의 변수를 부여하기 위해 여러 번 반복 될 수있다.
오버로드 된 접근 방식의 유용한 예를 공유하고 싶습니다.
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에서 참조한 패널을 지 웁니다.
JavaScript는 형식화되지 않은 언어이므로 매개 변수 수와 관련하여 메서드 / 함수를 오버로드하는 것이 좋습니다. 따라서 매개 변수가 정의되어 있는지 확인하는 것이 좋습니다.
myFunction = function(a, b, c) {
if (b === undefined && c === undefined ){
// do x...
}
else {
// do y...
}
};
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 ...
}
하지만 기본 매개 변수가 과부하되지, 그것은 개발자가이 분야에서 직면하고있는 몇 가지 문제를 해결할 수 있습니다. 입력은 주문에 의해 엄격하게 결정되며 클래식 과부하와 같이 원하는대로 다시 주문할 수 없습니다.
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