Подтвердить что ты не робот

Как лучше всего реализовать параметры в JavaScript?

Я использую Javascript с jQuery. Я хотел бы реализовать параметры. В С# он будет выглядеть примерно так:

/*
 * odp      the object to test
 * error    a string that will be filled with the error message if odp is illegal. Undefined otherwise.
 *
 * Returns  true if odp is legal.
 */
bool isLegal(odp, out error);

Каков наилучший способ сделать что-то подобное в JS? Объекты?

function isLegal(odp, errorObj)
{
    // ...
    errorObj.val = "ODP failed test foo";
    return false;
}

Firebug говорит мне, что вышеупомянутый подход будет работать, но есть ли лучший способ?

4b9b3361

Ответ 1

Подход обратного вызова, упомянутый @Felix Kling, вероятно, лучшая идея, но я также обнаружил, что иногда легко использовать синтаксис литералов для объектов Javascript и просто вернуть функцию к ошибке:

function mightFail(param) {
  // ...
  return didThisFail ? { error: true, msg: "Did not work" } : realResult;
}

то при вызове функции:

var result = mightFail("something");
if (result.error) alert("It failed: " + result.msg);

Не фантазия и вряд ли пуленепробиваемая, но, конечно, это нормально для некоторых простых ситуаций.

Ответ 2

Да, как вы сами упоминали, объекты - лучший и единственный способ передать данные по ссылке в JavaScript. Я бы сохранил вашу функцию isLegal как таковую и просто назову ее так:

var error = {};
isLegal("something", error);
alert(error.val);

Ответ 3

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

То, что вы могли бы также рассмотреть, - использовать функцию обратного вызова:

function onError(data) {
    // do stuff
}


function isLegal(odp, cb) {
    //...
    if(error) cb(error);
    return false;
}

isLegal(value, onError);

Ответ 4

существует другой способ, которым JS может передавать параметры "out". но я считаю, что лучшие из них для вашей ситуации уже упоминались.

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

поэтому в каждой ситуации вы можете спросить себя: "Является ли массив или объект лучше?"

Ответ 5

Ответы, которые я видел до сих пор, не реализуют параметры out в JavaScript, так как они используются в С# (ключевое слово out). Это просто обходной путь, который возвращает объект в случае ошибки.

Но что вы будете делать, если вам действительно нужны параметры?

Поскольку Javascript не поддерживает его напрямую, вам нужно создать нечто, близкое к параметрам С# out. Взгляните на этот подход, я эмулирую функцию DateTime.TryParse в С# в JavaScript. Параметр out является результатом, и поскольку JavaScript не предоставляет ключевое слово out, я использую .value внутри функции для передачи значения за пределы функции (как вдохновлено предложением MDN):

// create a function similar to C# DateTime.TryParse
var DateTime = [];
DateTime.TryParse = function(str, result) {
  result.value = new Date(str); // out value
  return (result.value != "Invalid Date");
};

// now invoke it
var result = [];
if (DateTime.TryParse("05.01.2018", result)) {
  alert(result.value);
} else {
  alert("no date");
};

Ответ 6

Я использую метод обратного вызова (аналогичный подход Феликса Клинга), чтобы имитировать поведение выходных параметров. Мой ответ отличается от Kling тем, что функция обратного вызова действует как замыкание ссылки, а не обработчик.

Этот подход страдает синтаксисом синтаксических анонимных функций JavaScript, но тесно воспроизводит семантику параметров с других языков.

function isLegal(odp, out_error) {
    //...
    out_error("ODP failed test foo"); // Assign to out parameter.
    return false;
}

var error;
var success = isLegal(null, function (e) { error = e; });

// Invariant: error === "ODP failed test foo".

Ответ 7

Я не буду публиковать какие-либо code, но то, что здесь не может быть сделано в этих ответах, - это поставить рифму для разума. Я работаю на родной арене JS, и возникла проблема, что некоторые native API calls нужно преобразовать, потому что мы не можем писать параметры без уродливых постыдных хаков.

Это мое решение:

    // Functions that return parameter data should be modified to return
    // an array whose zeroeth member is the return value, all other values
    // are their respective 1-based parameter index.

Это не означает определение и возврат каждого параметра. Только параметры, которые получают выход.

Причиной такого подхода является: Multiple return values может потребоваться для любого количества процедур. Это создает ситуацию, когда объекты с именованными значениями (которые в конечном счете не будут синхронизироваться с лексическим контекстом всех операций), постоянно необходимо запомнить, чтобы надлежащим образом работать с процедурами (процедурами).

Используя предписанный метод, вам нужно знать what you called и where you should be looking, а не , чтобы знать, что вы ищете.

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

Было бы разумно использовать object, function или array (все из которых являются объектами) как параметр "write-back-output-output", но я считаю, что если какая-либо посторонняя работа должна это должно быть сделано тем, кто пишут инструментарий, чтобы облегчить задачу или расширить функциональность.

Это ответ на каждый ответ за каждую случайность, который держит APIs, глядя так, как должно выглядеть сначала, а не появляться, и иметь все сходство из-за того, что копченое переплетение шпагата-ковра гобелена, который не может фигурировать если это определение или данных.

Поздравляем и удачи.

Я использую webkitgtk3 и подключаю некоторые собственные C-библиотеки. поэтому этот проверенный образец кода может по крайней мере служить цели иллюстрации.

// ssize_t read(int filedes, void *buf, size_t nbyte)
SeedValue libc_native_io_read (SeedContext ctx, SeedObject function, SeedObject this_object, gsize argument_count, const SeedValue arguments[], SeedException *exception) {


    // NOTE: caller is completely responsible for buffering!

                    /* C CODING LOOK AND FEEL */


    if (argument_count != 3) {
        seed_make_exception (ctx, exception, xXx_native_params_invalid,
            "read expects 3 arguments: filedes, buffer, nbyte: see `man 3 read' for details",
            argument_count
        );  return seed_make_undefined (ctx);
    }

    gint filedes = seed_value_to_int(ctx, arguments[0], exception);
    void *buf = seed_value_to_string(ctx, arguments[1], exception);
    size_t nbyte = seed_value_to_ulong(ctx, arguments[2], exception);

    SeedValue result[3];

    result[0] = seed_value_from_long(ctx, read(filedes, buf, nbyte), exception);
    result[2] = seed_value_from_binary_string(ctx, buf, nbyte, exception);

    g_free(buf);
    return  seed_make_array(ctx, result, 3, exception);

}

Ответ 8

Ниже приведен подход, который я использую. И это ответ на этот вопрос. Однако код не был протестирован.

function mineCoords( an_x1, an_y1 ) {
  this.x1 = an_x1;
  this.y1 = an_y1;
}

function mineTest( an_in_param1, an_in_param2 ) {

  // local variables
  var lo1 = an_in_param1;
  var lo2 = an_in_param2;

  // process here lo1 and lo2 and 
  // store result in lo1, lo2

  // set result object
  var lo_result = new mineCoords( lo1, lo2 );
  return lo_result;
}

var lo_test = mineTest( 16.7, 22.4 );
alert( 'x1 = ' + lo_test.x1.toString() + ', y1 = ' + lo_test.y1.toString() );

Ответ 9

Обычный подход к конкретному варианту использования, который вы описали в Javascript, и на самом деле на большинстве языков высокого уровня, заключается в том, чтобы полагаться на Ошибки (aka exceptions), чтобы вы знали, когда произошло что-то необычное. Нет способа передать тип значения (строки, числа и т.д.) По ссылке в Javascript.

Я бы просто сделал это. Если вам действительно нужно передать пользовательские данные обратно вызывающей функции, вы можете подклассифицировать Error.

var MyError = function (message, some_other_param)
{
    this.message = message;
    this.some_other_param = some_other_param;
}
//I don't think you even need to do this, but it makes it nice and official
MyError.prototype = Error; 
...
if (something_is_wrong)
    throw new MyError('It failed', /* here a number I made up */ 150); 

Захват исключений - это боль, я знаю, но потом снова так отслеживает ссылки.

Если вам действительно нужно что-то, что подходит к поведению внешних переменных, объекты передаются по ссылке по умолчанию и могут легко захватывать данные из других областей -

function use_out (outvar)
{
    outvar.message = 'This is my failure';
    return false;
}

var container = { message : '' };
var result = use_out(container );
console.log(container.message); ///gives the string above
console.log(result); //false

Я думаю, что это несколько способ ответить на ваш вопрос, но я думаю, что весь ваш подход сломан с самого начала. Javascript поддерживает гораздо более элегантные и мощные способы получения нескольких значений из функции. Некоторые чтения о генераторах, замыканиях, ада даже обратные вызовы могут быть приятными в определенных ситуациях - искать стиль продолжения прохождения.

Моя точка зрения заключается в том, чтобы кто-нибудь читал это, чтобы адаптировать свой стиль программирования к ограничениям и возможностям используемого языка, вместо того, чтобы пытаться заставить то, чему они учились на других языках.

(Кстати, некоторые люди настоятельно рекомендуют против закрытия, потому что они вызывают злые побочные эффекты , но я их не слушал. Они пуристы. Побочные эффекты почти неизбежны во многих приложениях без большого утомительного отступления и преодоления препятствий. Если они вам понадобятся, все они вместе в аккуратном лексическом пространстве, а не разбросаны по hellscape неясных указателей, и ссылки звучат намного лучше, чем я)