web-dev-qa-db-ja.com

JavaScriptでoutパラメータを実装する最善の方法は?

JQueryでJavascriptを使用しています。 paramsを実装したいと思います。 C#では、次のようになります。

/*
 * 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は、上記のアプローチはうまくいくと言っていますが、もっと良い方法はありますか?

34
Nick Heiner

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

派手ではなく、ほとんど防弾ではありませんが、確かにいくつかの単純な状況では問題ありません。

42
Pointy

はい、あなた自身が述べたように、オブジェクトはJavaScriptで参照によってデータを渡すための最良かつ唯一の方法です。 isLegal関数をそのままにして、次のように呼び出すだけです。

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

これがほとんど唯一の方法だと思います(しかし、私は筋金入りのJavaScriptプログラマーではありません;))。

また、コールバック関数を使用することも検討できます。

function onError(data) {
    // do stuff
}


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

isLegal(value, onError);
15
Felix Kling

私がこれまで見てきた答えは、C#(outキーワード)で使用されているため、JavaScriptのoutパラメーターを実装していません。エラーの場合にオブジェクトを返す回避策にすぎません。

ただし、出力パラメータが本当に必要な場合はどうしますか?

Javascriptは直接サポートしないため、C#の出力パラメーターに近いものを作成する必要があります。このアプローチを見てください。JavaScriptでC#s DateTime.TryParse関数をエミュレートしています。 outパラメーターは結果であり、JavaScriptはoutキーワードを提供しないため、関数の内部で.valueを使用して関数の外部に値を渡します( MDN提案 に触発されたとおり):

// create a function similar to C#'s 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");
};

スニペットを実行すると、動作することがわかります。strパラメーターを解析して日付に変換し、resultパラメーターで返します。 result空の配列[] before として初期化する必要があることに注意してください。関数内に.valueプロパティを「注入」するため、これが必要です。


これで、上記のパターンを使用して質問の関数として記述できます(これはout _と呼ばれる新しい discard パラメーターをエミュレートする方法も示します) C#の場合:JavaScriptでは、以下に示すように[]を渡します)。

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

// returns false, if odb is no date, otherwise true
function isLegal(odp, errorObj) {
  if (DateTime.TryParse(odp, [])) { // discard result here by passing []
    // all OK: leave errorObj.value undefined and return true
    return true;
  } else {
    errorObj.value = "ODP failed test foo"; // return error
    return false;
  }
}

// now test the function
var odp = "xxx01.12.2018xx"; // invalid date
var errorObj = [];
if (!isLegal(odp, errorObj)) alert(errorObj.value); else alert("OK!");

この例では、resultパラメーターを使用して、次のようにエラーメッセージを渡します。

errorObj.value = "ODPテストfooに失敗しました"; //エラーを返します

この例を実行すると、ポップアップダイアログにこのメッセージが表示されます。

注意:上記のように破棄パラメーターを使用する代わりに、JavaScriptでundefinedのチェックを使用することもできます。

if (result === undefined) { 
   // do the check without passing back a value, i.e. just return true or false 
};

その後、不要であればresultをパラメーターとして完全に省略することができるため、次のように呼び出すことができます。

if (DateTime.TryParse(odp)) { 
    // ... same code as in the snippet above ...
};
3
Matt

jSが「出力」パラメータを渡すことができる別の方法があります。しかし、私はあなたの状況に最適なものがすでに言及されていると信じています。

配列も値ではなく参照によって渡されます。したがって、オブジェクトを関数に渡し、その関数のオブジェクトのプロパティを設定し、そのオブジェクトのプロパティに戻り、アクセスできるのと同様に、配列を関数に渡し、配列のいくつかの値を設定できます関数内で、配列外の値を返しアクセスします。

そのため、それぞれの状況で、「配列またはオブジェクトのどちらが良いですか」と自問することができます。

コールバックメソッド( Felix 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".
2
Gooseberry

以下は私が使用しているアプローチです。これがこの質問に対する答えです。ただし、コードはテストされていません。

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() );
1
Dominik Lebar

codeを投稿するつもりはありませんが、これらの回答でここで実行できないのは、韻を踏んで理由です。私はネイティブのJSアリーナで働いていますが、いくつかのnative API calls transformedい恥ずべきハックなしではパラメータに書き込むことができないため、変換する必要があります。

これは私の解決策です:

    // 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は、任意の数の手順で必要になる場合があります。これにより、名前付きの値を持つオブジェクト(最終的にはすべての操作の字句コンテキストと同期しない)、constantlyを適切に処理するために記憶する必要がある状況が生じます。

規定の方法を使用すると、what you called、およびwhere you should be lookingではなく、探しているものを知る必要がある

また、「robust and stupid」というalogrithmsを記述して、目的のプロシージャコールをラップして、この操作を「透過的」にすることができるという利点もあります。

「ライトバック出力」パラメーターとしてobjectfunction、またはarray(すべてオブジェクト)を使用するのが賢明でしょうが、私は信じています余分な作業を行う必要がある場合は、ツールキットを作成する人が作業を簡単にするか、機能を拡張することで行う必要があります。

これは、すべての機会に対するすべての答えのためのものであり、APIsは見た目が見えないように見え、把握できないスパゲッティコードタペストリーの石畳のように見えるのではなく、 definitionまたはdataの場合、出力されます。

おめでとう、そして幸運。

私は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);

}
1
user735796

Javascriptで概説した特定のユースケースへの通常のアプローチ、および実際にはほとんどの高レベル言語では、エラー(別名例外)に依存して、通常とは異なる何かが発生したことを知らせます。 Javascriptの参照によって値の型(文字列、数値など)を渡す方法はありません。

私はそれをするだけです。呼び出し元の関数にカスタムデータをフィードバックする必要がある場合は、エラーをサブクラス化できます。

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's a number I made up */ 150); 

例外をキャッチするのは苦痛ですが、それでも参照を追跡するのは大変です。

out変数の動作にアプローチするものが本当に必要な場合、オブジェクトはデフォルトで参照によって渡され、他のスコープからデータを簡単にキャプチャできます-

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は、関数から複数の値を取得するための非常に多くのエレガントで強力な方法をサポートしています。ジェネレーター、クロージャー、地獄についてのいくつかの読書をしてください。特定の状況ではコールバックもニースになる可能性があります-継続渡しスタイルを調べます。

この大暴れの私のポイントは、他の言語から学んだことを強制しようとするのではなく、これを読んでいる人に、使用している言語の制限と機能にプログラミングスタイルを適応させることです。

(ところで、一部の人々は悪を引き起こすので閉鎖を強くお勧めします副作用、しかし私はそれらに耳を傾けません。彼らは純粋主義者です。副作用は多くのアプリケーションではほとんど避けられません。退屈なバックトラックとここからの障害物の回避の必要性。それらが必要な場合、それらをすべて曖昧なポインタと参照の地獄の風景に散らばるのではなく、きちんとした字句の範囲にまとめることは私にとってはるかに良い音です)

0
Chuck Bajax