Именованные параметры в 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 118

7 ответов:

ES2015

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

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

и это отлично работает, за исключением..... когда дело доходит до большинства IDE, многие из нас, разработчиков, полагаются на подсказки типа / аргумента в нашей IDE. Я лично использую PHP Storm (наряду с другими JetBrains IDE, такими как 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') и узел 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