How best to implement out params in JavaScript?

后端 未结 9 1580
盖世英雄少女心
盖世英雄少女心 2020-12-15 03:36

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            


        
相关标签:
9条回答
  • 2020-12-15 04:12

    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".
    
    0 讨论(0)
  • 2020-12-15 04:13

    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)

    0 讨论(0)
  • 2020-12-15 04:17

    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 ...
    };
    
    0 讨论(0)
提交回复
热议问题