How best to implement out params in JavaScript?

2020-02-08 23:06发布

I'm using Javascript with jQuery. I'd like to implement out params. In C#, it would look something like this:

/*
 * 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);

What is the best way to do something like this in JS? Objects?

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

Firebug tells me that the above approach would work, but is there a better way?

9条回答
ゆ 、 Hurt°
2楼-- · 2020-02-08 23:46

there is another way JS can pass 'out' parameters. but i believe the best ones for your situation were already mentioned.

Arrays are also passed by reference, not value. thus just as you can pass an object to a function, and then set a property of the object in the function, and then return, and access that object's property, you can similarly pass an Array to a function, set some values of the array inside the function, and return and access those values outside the array.

so in each situation you can ask yourself, "is an array or an object better?"

查看更多
虎瘦雄心在
3楼-- · 2020-02-08 23:48

I think this is pretty much the only way (but I am not a hardcore JavaScript programmer ;)).

What you could also consider is to use a callback function:

function onError(data) {
    // do stuff
}


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

isLegal(value, onError);
查看更多
我命由我不由天
4楼-- · 2020-02-08 23:52

The following is approach i am using. And this is answer for this question. However code has not been tested.

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() );
查看更多
该账号已被封号
5楼-- · 2020-02-08 23:53

I'm not going to post any code but what fails to be done here in these answers is to put rhyme to reason. I'm working in the native JS arena and the problem arose that some native API calls need to be transformed because we can't write to the parameters without ugly shameful hacks.

This is my solution:

    // 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.

That doesn't mean define and return every parameter. Only the parameters that recieve output.

The reason for this approach is thus: Multiple return values may be needed for any number of procedures. This creates a situation where objects with named values (that ultimately will not be in sync with the lexical context of all operations), constantly need to be memorized in order to appropriately work with the procedure(s).

Using the prescribed method, you only have to know what you called, and where you should be looking rather than having to know what you are looking for.

There is also the advantage that "robust and stupid" alogrithms can be written to wrap around the desired procedure calls to make this operation "more transparent".

It would be wise to use an object, function, or an array (all of which are objects) as a "write-back-output" parameter, but I believe that if any extraneous work must be done, it should be done by the one writing the toolkit to make things easier, or broaden functionality.

This is a one for all answer for every occaision, that keeps APIs looking the way the should at first look, rather than appearing to be and having every resemblence of a hobble-cobbled weave of spaghetti code tapestry that cannot figure out if it is a definition or data.

Congratulations, and good luck.

I'm using the webkitgtk3 and interfaceing some native C Library procs. so this proven code sample might at least serve the purpose of illustration.

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

}
查看更多
别忘想泡老子
6楼-- · 2020-02-09 00:00

Yes, as you yourself mentioned, objects are the best and only way to pass data by reference in JavaScript. I would keep your isLegal function as such and simply call it like this:

var error = {};
isLegal("something", error);
alert(error.val);
查看更多
祖国的老花朵
7楼-- · 2020-02-09 00:01

The answers I have seen so far aren't implementing out parameters in JavaScript, as they are used in C# (the out keyword). They are merely a workaround that returns an object in case of an error.

But what do you do if you really need out parameters?

Because Javascript doesn't directly support it, you need to build something that is close to C#'s out parameters. Take a look at this approach, I am emulating C#s DateTime.TryParse function in JavaScript. The out parameter is result, and because JavaScript doesn't provide an out keyword, I am using .value inside the function to pass the value outside the function (as inspired by MDN suggestion):

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

Run the snippet and you'll see it works: It parses the str parameter into a Date and returns it in the result parameter. Note that result needs to be initialized as empty array [], before you call the function. This is required because inside the function you "inject" the .value property.


Now you can use the pattern above to write a function as the one in your question (this also shows you how to emulate the new discard parameter known as out _ in C#: In JavaScript we're passing [] as shown below):

// 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!");

What this example does is it uses the result parameter to pass an error message as follows:

errorObj.value = "ODP failed test foo"; // return error

If you run the example it will display this message in a popup dialog.

Note: Instead of using a discard parameter as shown above, in JavaScript you could also use a check for undefined, i.e. inside the function check for

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

Then it is possible to omit result as a parameter completely if not needed, so you could invoke it like

if (DateTime.TryParse(odp)) { 
    // ... same code as in the snippet above ...
};
查看更多
登录 后发表回答