JavaScript에서 함수 호출에 명명 된 매개 변수를 제공하는 방법이 있습니까?


207

C #의 명명 된 매개 변수 기능은 경우에 따라 매우 유용합니다.

calculateBMI(70, height: 175);

JavaScript로 이것을 원한다면 무엇을 사용할 수 있습니까?


내가 원하지 않는 것은 이것입니다.

myFunction({ param1: 70, param2: 175 });

function myFunction(params){
  // Check if params is an object
  // Check if the parameters I need are non-null
  // Blah blah
}

내가 이미 사용한 접근법. 다른 방법이 있습니까?

라이브러리를 사용 하여이 작업을 수행 할 수 있습니다.


나는 이것이 가능하다고 생각하지 않지만 빈 공간에 정의되지 않은 것을 넣을 수 있습니다. 어느 것이 나쁘다. 좋은 물건을 사용하십시오.
Vladislav Qulin

14
아니요, JavaScript / EcmaScript는 명명 된 매개 변수를 지원하지 않습니다. 죄송합니다.
smilly92

1
나는 이미 그것을 알고있다. 감사. FunctionJavascript 의 기존 기능 을 조정할 수있는 방법을 찾고있었습니다 .
Robin Maben

1
기존 FunctionJavascript는 Javascript의 핵심 구문을 변경할 수 없습니다.
Gareth

2
Javascript 가이 기능을 지원하지 않는다고 생각합니다. 명명 된 매개 변수에 가장 근접 할 수있는 것은 (1) 주석을 추가하거나 calculateBMI(70, /*height:*/ 175);(2) 객체를 제공 calculateBMI(70, {height: 175})하거나 (3) 상수를 사용하는 것 const height = 175; calculateBMI(70, height);입니다.
tfmontague

답변:


212

ES2015 이상

ES2015에서는 매개 변수 파괴 를 사용하여 명명 된 매개 변수를 시뮬레이션 할 수 있습니다. 호출자가 객체를 전달해야하지만 기본 매개 변수를 사용하는 경우 함수 내부의 모든 검사를 피할 수 있습니다.

myFunction({ param1 : 70, param2 : 175});

function myFunction({param1, param2}={}){
  // ...function body...
}

// Or with defaults, 
function myFunc({
  name = 'Default user',
  age = 'N/A'
}={}) {
  // ...function body...
}

ES5

원하는 것에 가깝게 접근 할 수있는 방법이 있지만 , 구현에 어느 정도 의존하는 Function.prototype.toString [ES5] 출력을 기반으로 하므로 브라우저 간 호환되지 않을 수 있습니다.

아이디어는 객체의 속성을 해당 매개 변수와 연결할 수 있도록 함수의 문자열 표현에서 매개 변수 이름을 구문 분석하는 것입니다.

함수 호출은 다음과 같습니다

func(a, b, {someArg: ..., someOtherArg: ...});

위치 인수는 어디 a이며 b마지막 인수는 인수가 명명 된 객체입니다.

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

var parameterfy = (function() {
    var pattern = /function[^(]*\(([^)]*)\)/;

    return function(func) {
        // fails horribly for parameterless functions ;)
        var args = func.toString().match(pattern)[1].split(/,\s*/);

        return function() {
            var named_params = arguments[arguments.length - 1];
            if (typeof named_params === 'object') {
                var params = [].slice.call(arguments, 0, -1);
                if (params.length < args.length) {
                    for (var i = params.length, l = args.length; i < l; i++) {
                        params.push(named_params[args[i]]);
                    }
                    return func.apply(this, params);
                }
            }
            return func.apply(null, arguments);
        };
    };
}());

당신은 다음과 같이 사용할 것입니다 :

var foo = parameterfy(function(a, b, c) {
    console.log('a is ' + a, ' | b is ' + b, ' | c is ' + c);     
});

foo(1, 2, 3); // a is 1  | b is 2  | c is 3
foo(1, {b:2, c:3}); // a is 1  | b is 2  | c is 3
foo(1, {c:3}); // a is 1  | b is undefined  | c is 3
foo({a: 1, c:3}); // a is 1  | b is undefined  | c is 3 

데모

이 접근 방식 에는 몇 가지 단점 이 있습니다 (경고되었습니다!).

  • 마지막 인수가 객체 인 경우 "명명 된 인수 객체"로 취급됩니다.
  • 함수에서 정의한만큼 많은 인수를 얻을 수 있지만 그 중 일부에는 값이있을 수 있습니다 (값 undefined이없는 것과는 다름). 즉, arguments.length몇 개의 인수가 전달되었는지 테스트 할 수 없습니다 .

랩퍼를 작성하는 함수 대신 함수와 다양한 값을 인수로 허용하는 함수를 가질 수도 있습니다.

call(func, a, b, {posArg: ... });

또는 다음과 같이 할 수 Function.prototype있도록 확장하십시오 .

foo.execute(a, b, {posArg: ...});

예 ... 여기에 jsfiddle.net/9U328/1의 예가 있습니다 (단, 사용 Object.defineProperty하고로 설정 enumerable해야 함 false). 기본 객체를 확장 할 때는 항상주의해야합니다. 전체 접근 방식은 약간 해키처럼 느껴지므로 지금은 영원히 작동하지 않을 것입니다.)
Felix Kling

유명한. 나는 이것을 사용하도록 내릴 것이다. 답변으로 표시! ... 지금은;)
Robin Maben

1
아주 작은 nitpick : 나는이 접근법이 EcmaScript 6 Arrow Functions를 잡을 것이라고 생각하지 않습니다 . 현재 큰 문제는 아니지만 답변의주의 사항 섹션에서 언급 할 가치가 있습니다.
아무도 Man

3
@NobodyMan : 맞습니다. 화살표 기능이 있기 전에이 답변을 썼습니다. ES6에서는 실제로 매개 변수 파괴에 의존합니다.
Felix Kling 2016 년

1
undefinedvs "값 없음" 문제 에서는 이것이 JS 함수의 기본값이 작동 undefined하는 방식 (결 측값으로 취급) 과 정확히 일치한다고 덧붙일 수 있습니다 .
Dmitri Zaitsev

71

아니요 -객체 접근 방식은 이에 대한 JavaScript의 답변입니다. 함수가 별도의 매개 변수가 아닌 객체를 기대하는 경우에는 아무런 문제가 없습니다.


35
@RobertMaben-특정 질문에 대한 답변은 특정 네임 스페이스에 살고 있음을 알지 못하고 선언 된 var 또는 함수를 수집하는 기본 방법이 없다는 것입니다. 대답이 짧기 때문에 답으로서의 적합성을 부정하지 않습니다. 동의하지 않습니까? "아니오, 불가능"의 행을 따라 훨씬 짧은 답변이 있습니다. 짧을 수도 있지만 질문에 대한 답변이기도합니다.
Mitya

3
현재는 es6가 있습니다 : 2ality.com/2011/11/keyword-parameters.html
slacktracer

1
이것은 분명히 정답입니다. '명명 된 매개 변수'는 언어 기능입니다. 이러한 기능이 없으면 객체 접근 방식이 다음으로 가장 좋습니다.
fatuhoku

32

이 문제는 한동안 내 애완 동물이었던 것입니다. 나는 내 벨트 아래에 많은 언어를 가진 노련한 프로그래머입니다. 내가 가장 좋아하는 언어 중 하나는 Python입니다. 파이썬은 속임수없이 명명 된 매개 변수를 지원합니다 .... 파이썬을 사용하기 시작한 이래 (모든 시간 전에) 모든 것이 쉬워졌습니다. 모든 언어가 명명 된 매개 변수를 지원해야한다고 생각하지만, 그렇지 않습니다.

많은 사람들이 매개 변수 이름을 지정하기 위해 "Pass an object"트릭을 사용한다고 말합니다.

/**
 * My Function
 *
 * @param {Object} arg1 Named arguments
 */
function myFunc(arg1) { }

myFunc({ param1 : 70, param2 : 175});

그리고 대부분의 IDE에 관해서는 많은 개발자들이 IDE 내에서 유형 / 인수 힌트에 의존합니다. 나는 개인적으로 PHP Storm을 사용합니다 (PyCharm for Python 및 AppCode for Objective C와 같은 다른 JetBrains IDE와 함께)

"Pass an object"트릭을 사용할 때 가장 큰 문제는 함수를 호출 할 때 IDE가 단일 유형 힌트를 제공한다는 것입니다. arg1 객체?

arg1에 어떤 매개 변수가 들어 가야할지 모르겠습니다.

그래서 ... "Pass an object"트릭이 저에게는 효과가 없습니다 ... 실제로 함수가 어떤 매개 변수를 기대하는지 알기 전에 각 함수의 docblock을 살펴 봐야하는 두통이 더 많이 생깁니다 .... 물론입니다. 기존 코드를 유지할 때 새 코드를 작성하는 것은 끔찍합니다.

글쎄, 이것은 내가 사용하는 기술입니다 .... 이제는 몇 가지 문제가있을 수 있으며 일부 개발자는 내가 잘못하고 있다고 말할 수 있습니다. 나는 항상 작업을 수행하는 더 나은 방법을 기꺼이 생각합니다 ... 따라서이 기술에 문제가 있으면 의견을 환영합니다.

/**
 * My Function
 *
 * @param {string} arg1 Argument 1
 * @param {string} arg2 Argument 2
 */
function myFunc(arg1, arg2) { }

var arg1, arg2;
myFunc(arg1='Param1', arg2='Param2');

이런 식으로, 나는 두 세계의 장점을 모두 가지고 있습니다 ... IDE에서 모든 적절한 인수 힌트를 제공하므로 새로운 코드를 작성하기 쉽습니다 ... 나중에 코드를 유지하면서 한 눈에 볼 수 있습니다. 함수에 전달 된 값뿐만 아니라 인수의 이름도 전달됩니다. 내가 보는 유일한 오버 헤드는 전역 네임 스페이스를 오염시키지 않도록 인수 이름을 로컬 변수로 선언하는 것입니다. 물론 약간의 추가 타이핑이지만 새 코드를 작성하거나 기존 코드를 유지하면서 docblock을 찾는 데 걸리는 시간과 비교하면 사소한 것입니다.

이제 새 코드를 만들 때 모든 매개 변수와 유형이 있습니다.


2
이 기술의 유일한 점은 매개 변수의 순서를 변경할 수 없다는 사실입니다 ... 개인적으로는 괜찮습니다.
Ray Perea

13
미래의 일부 관리자가 와서 인수 순서를 변경할 수 있다고 생각할 때 문제를 요구하는 것처럼 보입니다 (그러나 분명히 할 수는 없습니다).
아무도

1
@AndrewMedico 동의합니다 ... 파이썬에서와 같이 인수 순서를 변경할 수있는 것처럼 보입니다. 내가 말할 수있는 유일한 것은 인수 순서를 변경하면 프로그램이 깨질 때 실제로 빨리 알 수 있다는 것입니다.
Ray Perea

7
나는 그것이 실제 명명 된 논쟁이 일어나고있는 것보다 독자를 속일 수있는 기회가 없기 때문에 myFunc(/*arg1*/ 'Param1', /*arg2*/ 'Param2');보다 낫다고 주장한다myFunc(arg1='Param1', arg2='Param2');
Eric

2
제안 된 패턴은 명명 된 인수와 아무 관련이 없으며 순서는 여전히 중요하며 이름은 실제 매개 변수와 동기화 될 필요가 없으며 네임 스페이스는 불필요한 변수로 오염되며 존재하지 않는 관계는 암시됩니다.
Dmitri Zaitsev

25

단순히 호출하는 것이 아니라 각 매개 변수가 무엇인지 명확하게하려면

someFunction(70, 115);

왜 다음을하지 않습니까

var width = 70, height = 115;
someFunction(width, height);

추가 코드 줄이지 만 가독성이 좋습니다.


5
KISS 원칙을 준수하면 +1이되고 디버깅에도 도움이됩니다. 그러나 약간의 성능 저하 ( http://stackoverflow.com/questions/9672635/javascript-var-statement-and-performance ) 에도 불구하고 각 var는 자체 라인에 있어야한다고 생각합니다 .
clairestreb

21
추가 코드 줄뿐만 아니라 인수의 순서 및 선택 사항에 관한 것입니다. 따라서 명명 된 매개 변수를 사용하여 이것을 someFunction(height: 115);작성할 someFunction(height);수 있습니다. 하지만 글 을 쓰면 실제로 너비를 설정하는 것입니다.
Francisco Presencia

이 경우 CoffeeScript는 명명 된 인수를 지원합니다. 그냥 쓸 수 있습니다 someFunction(width = 70, height = 115);. 변수는 생성 된 JavaScript 코드에서 현재 범위의 맨 위에 선언됩니다.
Audun Olsen 2012

6

다른 방법은 적절한 객체의 속성을 사용하는 것입니다.

function plus(a,b) { return a+b; };

Plus = { a: function(x) { return { b: function(y) { return plus(x,y) }}}, 
         b: function(y) { return { a: function(x) { return plus(x,y) }}}};

sum = Plus.a(3).b(5);

물론이 예제에서는 다소 의미가 없습니다. 그러나 함수가 다음과 같은 경우

do_something(some_connection_handle, some_context_parameter, some_value)

더 유용 할 수 있습니다. 또한 "매개 변수화"아이디어와 결합하여 일반적인 방식으로 기존 함수에서 이러한 개체를 생성 할 수 있습니다. 즉, 각 매개 변수에 대해 부분 평가 버전의 함수로 평가할 수있는 멤버를 만듭니다.

이 아이디어는 물론 Schönfinkeling aka Currying과 관련이 있습니다.


이것은 깔끔한 아이디어이며 인수 내성 트릭과 결합하면 더 나아집니다. 불행히도, 선택적인 인수로 작업하기에는 부족합니다
Eric

2

다른 방법이 있습니다. 객체를 참조로 전달하는 경우 해당 객체의 속성이 함수의 로컬 범위에 나타납니다. 나는 이것이 Safari (다른 브라우저를 확인하지 않은)에서 작동한다는 것을 알고 있으며이 기능에 이름이 있는지 모르겠지만 아래 예제는 그 사용법을 보여줍니다.

실제로 나는 이것이 이미 사용중인 기술 이외의 기능적 가치를 제공한다고 생각하지 않지만 의미 적으로 조금 더 깨끗합니다. 그리고 여전히 객체 참조 또는 객체 리터럴을 전달해야합니다.

function sum({ a:a, b:b}) {
    console.log(a+'+'+b);
    if(a==undefined) a=0;
    if(b==undefined) b=0;
    return (a+b);
}

// will work (returns 9 and 3 respectively)
console.log(sum({a:4,b:5}));
console.log(sum({a:3}));

// will not work (returns 0)
console.log(sum(4,5));
console.log(sum(4));

2

Node-6.4.0 (process.versions.v8 = '5.0.71.60') 및 Node Chakracore-v7.0.0-pre8 및 Chrome-52 (V8 = 5.2.361.49)를 시도하면 명명 된 매개 변수가 거의 임을 알았습니다. 구현되었지만 그 순서가 여전히 우선합니다. ECMA 표준이 말하는 것을 찾을 수 없습니다.

>function f(a=1, b=2){ console.log(`a=${a} + b=${b} = ${a+b}`) }

> f()
a=1 + b=2 = 3
> f(a=5)
a=5 + b=2 = 7
> f(a=7, b=10)
a=7 + b=10 = 17

그러나 주문이 필요합니다! 표준 행동입니까?

> f(b=10)
a=10 + b=2 = 12

10
그것은 당신이 생각하는 것을하지 않습니다. b=10표현식 의 결과는 10입니다. 이것이 함수에 전달됩니다. f(bla=10)작동합니다 (변수에 10을 할당 bla하고 나중에 값을 함수에 전달합니다).
Matthias Kestenholz

1
이것은 또한 전역 범위에서 부작용으로 생성 a되고 b변수입니다.
Gershom

2

f객체로 전달 된 명명 된 매개 변수가있는 호출 함수

o = {height: 1, width: 5, ...}

기본적으로 f(...g(o))스프레드 구문을 사용하는 컴포지션을 호출하고 g객체 값을 매개 변수 위치와 연결하는 "바인딩"맵입니다.

바인딩 맵은 정확히 누락 된 성분이며 키 배열로 표시 될 수 있습니다.

// map 'height' to the first and 'width' to the second param
binding = ['height', 'width']

// take binding and arg object and return aray of args
withNamed = (bnd, o) => bnd.map(param => o[param])

// call f with named args via binding
f(...withNamed(binding, {hight: 1, width: 5}))

함수, 명명 된 인수가있는 객체 및 바인딩 의 세 가지 분리 된 구성 요소에 유의하십시오 . 이 디커플링은이 구성을 사용하기 위해 많은 유연성을 허용하는데, 여기서 바인딩은 함수 정의에서 임의로 사용자 정의되고 함수 호출시 임의로 확장 될 수 있습니다.

예를 들어, 당신은 생략 할 수 있습니다 heightwidth같이 h하고 w당신은 여전히 선명도에 대한 완전한 이름을 호출 할 때, 함수의 정의 내부가 짧고 청소기 만들기 위해 :

// use short params
f = (h, w) => ...

// modify f to be called with named args
ff = o => f(...withNamed(['height', 'width'], o))

// now call with real more descriptive names
ff({height: 1, width: 5})

이 유연성은 또한 원래의 매개 변수 이름을 잃어버린 상태에서 함수를 임의로 변환 할 수있는 함수형 프로그래밍에도 유용합니다.


0

파이썬에서 왔을 때이 버그가 발생했습니다. 위치 및 키워드 객체를 모두 허용하는 노드에 대한 간단한 래퍼 / 프록시를 작성했습니다.

https://github.com/vinces1979/node-def/blob/master/README.md


올바르게 이해하면 솔루션 정의에서 위치 및 명명 된 매개 변수를 구별해야하므로 호출 시이 선택을 자유롭게 할 수 없습니다.
Dmitri Zaitsev

@DmitriZaitsev : Python 커뮤니티 (키워드 인수가 일상적인 기능 임)에서는 사용자가 키워드로 선택적 인수를 지정할 수 있도록 하는 것이 좋습니다 . 이것은 오류를 피하는 데 도움이됩니다.
Tobias

JS에서 그렇게 강제 @Tobias 사용자는 문제 해결 : f(x, opt)여기서 opt목적으로한다. 오류를 피하거나 오류를 만드는 데 도움이되는지 (예 : 철자가 틀리거나 키워드 이름을 기억해야하는 어려움) 문제가 남아 있습니다.
Dmitri Zaitsev

@DmitriZaitsev : 파이썬에서는 키워드 인수가 핵심 언어 기능이므로 오류를 엄격하게 합니다. 키워드 전용 인수의 경우 Python 3에는 특수 구문이 있습니다 (Python 2에서는 kwargs dict 에서 키를 하나씩 팝하고 TypeError알 수없는 키가 남아 있으면 마지막으로 키를 올립니다 ). 귀하의 f(x, opt)솔루션을 사용 하면 f함수가 Python 2와 같은 기능을 수행 할 수 있지만 모든 기본값을 직접 처리해야합니다.
Tobias

@Tobias이 제안이 JS와 관련이 있습니까? 이 방법을 설명하는 것 같다 f(x, opt)예를 들어 당신이 두 가지를 모두 수행 할 위치 나는,이 질문에 대답하는 데 도움이 표시되지 않습니다 동안, 이미 작동 request(url)하고 request({url: url})그하여 불가능했을 것입니다 url키워드 전용 매개 변수.
Dmitri Zaitsev

-1

일반적으로 생각되는 것과는 달리, 명명 된 매개 변수는 아래와 같이 단순하고 깔끔한 코딩 규칙을 사용하여 표준 구식 JavaScript (부울 매개 변수의 경우에만)로 구현할 수 있습니다.

function f(p1=true, p2=false) {
    ...
}

f(!!"p1"==false, !!"p2"==true); // call f(p1=false, p2=true)

주의 사항 :

  • 인수의 순서는 유지되어야하지만 패턴은 여전히 ​​유용합니다. 함수 시그니처를 grep하거나 IDE를 사용할 필요없이 공식 매개 변수에 어떤 실제 인수가 사용되는지 명확하게 알 수 있기 때문입니다.

  • 이것은 부울에만 적용됩니다. 그러나 JavaScript의 고유 유형 강제 의미론을 사용하여 다른 유형에 대해서도 유사한 패턴을 개발할 수 있다고 확신합니다.


여기서는 이름이 아닌 직위로 계속 언급하고 있습니다.
Dmitri Zaitsev

@DmitriZaitsev 예, 심지어 위와 같이 말했습니다. 그러나 명명 된 인수의 목적은 각 인수가 의미하는 바를 독자에게 명확하게하는 것입니다. 그것은 문서의 한 형태입니다. 내 솔루션을 사용하면 주석에 의존하지 않고 함수 호출에 문서를 포함시킬 수 있습니다.
user234461

이것은 다른 문제를 해결합니다. 문제는 heightparam 순서에 관계없이 이름으로 전달 하는 것에 관한 것 입니다.
Dmitri Zaitsev

@DmitriZaitsev는 실제로 매개 변수 순서 또는 OP가 명명 된 매개 변수를 사용하려는 이유에 대해 질문하지 않았습니다.
user234461

어떤 순서로든 이름으로 매개 변수를 전달하는 것이 핵심 인 C #을 참조하여 수행합니다.
Dmitri Zaitsev
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.