Как лучше всего реализовать параметры в 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 говорит мне, что вышеупомянутый подход будет работать, но есть ли лучший способ?
Ответы
Ответ 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 неясных указателей, и ссылки звучат намного лучше, чем я)