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条回答
放荡不羁爱自由
2楼-- · 2020-02-09 00:03

I am using a callback method (similar to Felix Kling's approach) to simulate the behavior of out parameters. My answer differs from Kling's in that the callback function acts as a reference-capturing closure rather than a handler.

This approach suffers from JavaScript's verbose anonymous function syntax, but closely reproduces out parameter semantics from other languages.

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".
查看更多
叛逆
3楼-- · 2020-02-09 00:07

The callback approach mentioned by @Felix Kling is probably the best idea, but I've also found that sometimes it's easy to leverage Javascript object literal syntax and just have your function return an object on error:

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

then when you call the function:

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

Not fancy and hardly bulletproof, but certainly it's OK for some simple situations.

查看更多
姐就是有狂的资本
4楼-- · 2020-02-09 00:09

The usual approach to the specific use case you outlined in Javascript, and in fact most high level languages, is to rely on Errors (aka exceptions) to let you know when something out of the ordinary has occurred. There's no way to pass a value type (strings, numbers etc) by reference in Javascript.

I would just do that. If you really need to feed custom data back to the calling function you can subclass 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's a number I made up */ 150); 

Catching exceptions is a pain, I know, but then again so is keeping track of references.

If you really really need something that approaches the behavior of out variables, objects are passed by reference by default, and can handily capture data from other scopes--

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

I think this goes a some ways towards answering your question, but I think your entire approach is broken from the start. Javascript supports so many much more elegant and powerful ways to get multiple values out of a function. Do some reading about generators, closures, hell even callbacks can be nice in certain situations-- look up continuation passing style.

My point with this whole rant is to encourage anyone reading this to adapt their programming style to the limitations and capabilities of the language they're using, rather than trying to force what they learned from other languages into it.

(BTW some people strongly recommend against closures because they cause evil side-effects, but I wouldn't listen to them. They're purists. Side effects are almost unavoidable in a lot of applications without a lot of tedious backtracking and stepping around cant-get-there-from-here obstacles. If you need them, keeping them all together in a neat lexical scope rather than scattered across a hellscape of obscure pointers and references sounds a lot better to me)

查看更多
登录 后发表回答