如何最好地在JavaScript中实现参数?

如何最好地在JavaScript中实现参数?,javascript,out-parameters,Javascript,Out Parameters,我在jQuery中使用Javascript。我想实现我们的参数。在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 err

我在jQuery中使用Javascript。我想实现我们的参数。在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);
var error = {};
isLegal("something", error);
alert(error.val);
在JS中这样做的最佳方式是什么?物体

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

Firebug告诉我上面的方法可以工作,但是有更好的方法吗?

我认为这几乎是唯一的方法(但我不是一个硬核JavaScript程序员;)

您还可以考虑使用回调函数:

function onError(data) {
    // do stuff
}


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

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

@Felix Kling提到的回调方法可能是最好的方法,但我还发现,有时很容易利用Javascript对象文字语法,让函数在出错时返回对象:

function mightFail(param) {
  // ...
  return didThisFail ? { error: true, msg: "Did not work" } : realResult;
}
然后,当您调用该函数时:

function onError(data) {
    // do stuff
}


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

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

不是很花哨,也很难防弹,但对于一些简单的情况来说,这当然是可以的。

是的,正如您自己提到的,对象是JavaScript中通过引用传递数据的最佳且唯一的方法。我将保留您的
isLegal
功能,并简单地这样称呼它:

/*
 * 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);
var error = {};
isLegal("something", error);
alert(error.val);

JS可以通过另一种方式传递“out”参数。但我相信最适合你的情况已经提到了

数组也是通过引用而不是值传递的。因此,正如您可以将对象传递给函数,然后在函数中设置对象的属性,然后返回并访问该对象的属性一样,您也可以将数组传递给函数,在函数中设置数组的一些值,然后在数组外返回并访问这些值


因此,在每种情况下,你都可以问自己,“数组或对象更好吗?”

我不打算发布任何
代码,但在这些答案中,没有做到的是理清思路。我在原生JS领域工作,出现了一些
原生API调用
需要转换的问题,因为如果没有令人难堪的骇客,我们无法写入参数

这是我的解决方案:

    // 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.
这并不意味着定义并返回每个参数。只有 接收输出的参数

因此,采用这种方法的原因是:
任何数量的过程都可能需要多个返回值。这会造成这样一种情况,即具有命名值的对象(最终不会与所有操作的词法上下文同步),需要不断地记忆,以便正确地使用过程

使用规定的方法,您只需知道
您称之为什么
,以及
您应该在哪里查找
,而不必知道您正在查找什么

还有一个优点是,可以编写“健壮和愚蠢的”alogrithms来封装所需的过程调用,从而使此操作“更透明”

明智的做法是使用
对象
函数
、或
数组
(所有这些都是对象)作为“写回输出”参数,但我认为,如果必须完成任何无关的工作,则应该由编写工具包的人来完成,以使事情更简单,或扩展功能

这是一个针对每种情况的“一对一”解决方案,它保持了
API
一开始的样子,而不是看起来像是一个步履蹒跚的鹅卵石编织的意大利面代码挂毯,无法判断它是定义还是数据

祝贺你,祝你好运

我正在使用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);

}

以下是我正在使用的方法。这就是这个问题的答案。然而,代码还没有经过测试

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

我使用回调方法(类似于)来模拟out参数的行为。我的答案与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".

对于您在Javascript中概述的特定用例,实际上是大多数高级语言,通常的方法是依赖错误(也称为异常)来让您知道何时发生了异常情况。在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's a number I made up */ 150); 
捕获异常是一件痛苦的事,我知道,但跟踪引用也是如此

如果您真的需要接近out变量行为的东西,那么默认情况下对象是通过引用传递的,并且可以方便地从其他范围捕获数据--

我认为这有助于回答你的问题,但我认为你的整个方法从一开始就被打破了。Javascript支持许多更优雅、更强大的方法从函数中获取多个值。阅读一些关于生成器、闭包、甚至是回调的内容,在某些情况下可能会很好——查找延续传递样式

我的观点是鼓励所有阅读本文的人根据他们所使用语言的局限性和能力调整他们的编程风格,而不是试图将他们从其他语言中学到的东西强加给它

(顺便说一句,有些人强烈建议不要闭包,因为闭包会产生邪恶的副作用,但我不会听他们的。他们是纯粹主义者。在很多应用程序中,副作用几乎是不可避免的,没有太多繁琐的回溯和绕行障碍。如果需要,请将它们全部保留下来把她放在一个整洁的词法范围内,而不是分散在一个晦涩难懂的指针和引用的地狱场景中,对我来说听起来好多了)

String "55 parrots" is parsed as a positive integer 55.
String "-7 thoughts" is parsed as a non-positive integer -7.
String "several balls" does not start with an integer.
// Returns JavaScript for the defintion of a "pointer" to a variable named `v':
// The identifier of the pointer is that of the variable prepended by a $.
function makeref(v)
{   return `var $${v} = {set _(val){${v}=val;},get _() {return ${v};}}`;  }

// Calcualtes the square root of `value` and puts it into `$root`.
// Returns whether the operation has succeeded.
// In case of an error, stores error message in `$errmsg`.
function sqrt2
(   /* in  number */  value, /*  value to take the root of  */
    /* out number */ $root , /* "pointer" to result         */
    /* out string */ $errmsg /* "pointer" to error message  */
)
{   if( typeof( value ) !== "number" )
    {   $errmsg._ = "value is not a number.";
        return false;
    }
    if( value < 0 )
    {   $errmsg._ = "value is negative.";
        return false;
    }
    $root._ = Math.sqrt(value);
    return true;
}
function test(v)
{   var /* string */ resmsg;
    var /* number */ root  ; eval( makeref( "root"   ) );
    var /* string */ errmsg; eval( makeref( "errmsg" ) );
    
    if( sqrt2(v, $root, $errmsg) ) resmsg = `Success: ${root}`;
    else                           resmsg = `Error: ${errmsg}`;
    console.log(`Square root of ${v}: ` + resmsg );
}

test("s"  );
test(-5   );
test( 1.44);
Square root of s: Error: value is not a number.
Square root of -5: Error: value is negative.
Square root of 1.44: Success: 1.2
// Append string `sep' to a string pointed to by $s, using `sep` as separator:
// $s shall not point to an undefined value.
function append($s, sep, val)
{   if( $s._ != '' ) $s._ += sep;
    $s._ += val;
}
const sep = ", "
var s; eval( makeref("s") );

s = '';
append( $s, sep, "one"   );
append( $s, sep, "two"   );
append( $s, sep, "three" );
console.log( s );
one, two, three