Именованные параметры в javascript

Я считаю, что функция именованных параметров в 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
}

этот подход я уже использовал. Есть ли другой способ?

я в порядке, используя любую библиотеку. (Или кто-то может указать мне на тот, который уже делает)

7 ответов


ES2015

в ES2015, параметр destructuring может использоваться для имитации именованных параметров. Это потребует от абонента сдать объект, но вы можете избежать всех проверок внутри функции, если вы используете параметры по умолчанию:

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

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

в 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: ...});

нет - объектный подход является ответом JavaScript на это. С этим нет проблем, если ваша функция ожидает объект, а не отдельные параметры.


этот вопрос был моим домашним животным в течение некоторого времени. Я опытный программист, у меня за плечами много языков. Один из моих любимых языков, который я имел удовольствие использовать, - Python. Python поддерживает именованные параметры без обмана.... С тех пор, как я начал использовать Python (некоторое время назад), все стало проще. Я считаю, что каждый язык должен поддерживать именованные параметры, но это не так.

много людей говорят, чтобы просто использовать " передать объект" трюк, чтобы у вас были названные параметры.

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

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

и это отлично работает, за исключением..... когда дело доходит до большинства IDEs, многие из нас, разработчиков, полагаются на подсказки типа / аргумента в нашей IDE. Я лично использую PHP Storm (наряду с другими JetBrains IDEs, такими как PyCharm для python и AppCode для Objective C)

и самая большая проблема с использованием трюка "передать объект" заключается в том, что при вызове функции IDE дает вам подсказку одного типа, и это все... Как мы должны знать, какие параметры и типы должны входить в объект arg1?

I have no idea what parameters should go in arg1

Так... трюк "Передай предмет" мне не подходит... Это на самом деле вызывает больше головных болей с необходимостью смотреть на 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 дает мне все правильные подсказки аргументов... И, поддерживая код позже, я могу видеть с первого взгляда, не только значение, переданное функции, а также имя аргумента. Единственное, что я вижу, - это объявление имен аргументов в качестве локальных переменных, чтобы не загрязнять глобальное пространство имен. Конечно, это немного дополнительная типизация, но тривиальная по сравнению со временем, необходимым для поиска docblocks при написании нового кода или поддержании существующего кода.

Now, I have all the parameters and types when creating new code


Если вы хотите, чтобы было ясно, что каждый из параметров, а не просто вызов

someFunction(70, 115);

почему бы не сделать следующее

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

конечно, это дополнительная строка кода, но она выигрывает при удобочитаемости.


другой способ-использовать атрибуты подходящего объекта, например, так:

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)

это может быть более полезным. Он также может быть объединен с идеей "parameterfy" для создания такого объекта из существующей функции в общем виде. То есть для каждого параметра он создаст элемент, который может вычислить частичную оцененную версию функция.

эта идея, конечно, связана с Schönfinkeling ака Карринга.


есть другой способ. Если вы передаете объект по ссылке, свойства этого объекта будут отображаться в локальной области функции. Я знаю, что это работает для 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));

попытка узла-6.4.0 (процесс.версии.v8 = '5.0.71.60') и узел Чакракора-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