为什么Node.js函数接受对象作为参数?

时间:2013-01-28 19:39:59

标签: javascript node.js

这可能是一个JavaScript问题,但在Node.js中,我经常看到模块或方法将“options”对象作为其参数。举一个例子,我正在讨论的内容,请查看以下来自Node.js API文档的 http.request()方法。

var options = {
  hostname: 'www.google.com',
  port: 80,
  path: '/upload',
  method: 'POST'
};

var req = http.request(options, function(res) {
  console.log('STATUS: ' + res.statusCode);
  console.log('HEADERS: ' + JSON.stringify(res.headers));
  res.setEncoding('utf8');
  res.on('data', function (chunk) {
    console.log('BODY: ' + chunk);
  });
});

req.on('error', function(e) {
  console.log('problem with request: ' + e.message);
});

// write data to request body
req.write('data\n');
req.write('data\n');
req.end();

我在许多编程语言中看到的问题之一是参数被传递而没有区别什么值被认为对应于什么。这是一种用于简化可读性的技术,还是我缺少一个潜在的概念?我可以在PHP等其他语言中合理地使用相同的技术吗? (当然是使用关联数组而不是对象)

4 个答案:

答案 0 :(得分:11)

您当然可以在其他语言中使用此技术。我不能说它在JavaScript中可能具有的任何结构改进,但据我所知,这个想法是减少输入参数的数量。

作为一种风格问题(可读性,可支持性,软件应该具备的所有直观的东西),这里遵循的基本“规则”是更少的方法参数比许多方法参数更好。如果并非所有参数都是必需的,那么尤其为真。

考虑一个静态类型语言的例子,C#:

public Widget WidgetFactory(int widgetNumber, string widgetName, bool isActive, Widget parentWidget, List<Widget> childWidgets)
{
    // parentWidget may be null if there's no parent
    // childWidgets may be empty or null for no children
    // etc.
}

对于更复杂的对象,这可能会使非常丑陋非常快。想象一下大量的可选(可空)参数。 (如果您在4.0之前使用过.NET中的COM互操作,则无需想象它。)想象一下,如果您有多个需要这些参数的函数。再一次,它变得很难看。

因此,使用的模式是将所有选项封装到另一个对象中,该对象的唯一责任是维护这些选项:

public class WidgetCreationOptions
{
    public int WidgetNumber;
    public string WidgetName;
    // etc.
}

...别处

public Widget WidgetFactory(WidgetCreationOptions options)
{
    // etc.
}

随着选项变得越来越复杂并且内部包含相互引用的逻辑,将它们抽象到自己的对象中越来越有意义。这是通常的OO实践,用于移动许多小的简单对象而不是较少的大对象。

事实上,你在这句话中绝对正确:

  

我在许多编程语言中看到的一个问题是,参数传递时没有区分什么值可以对应什么。

当涉及到许多方法参数时,有许多“代码味道”:

  • 参数太多。
  • 布尔标志作为参数(表示被调用的方法不止一件事)
  • 可以为空的论点(如果你不需要它,为什么需要呢?)

目前可能还有更多让我逃避的事情。但是,正如你已经确定的那样,这很难理解:

someObject.SomeFunction(1, false, null, "this is a string", false);

然而,这更加明确:

var options = {
    widgetNumber: 1,
    isActive: false,
    widgetName: "this is a string"
};

// ... elsewhere ...

someObject.SomeFunction(options);

答案 1 :(得分:2)

这是JavaScript中非常常见的模式,因为函数可以更松散地耦合。说起来容易得多:

doStuff( { someValue: 1, anotherValue: 2 } );

而不是:

doStuff(1, null, null, null, 2);

前一种设计有两个优点:

  1. 它更容易阅读,并且更清楚传递了什么值。我可以省略我不想指定的参数,而不必传入空值。我还得到一个提示关于参数是什么,因为它用键标记。
  2. 功能签名可以更改,添加更多密钥,而不会破坏现有代码。我可能会删除anotherValue,我可以忽略该密钥,而不必在函数签名中保留一个空白参数。

答案 2 :(得分:1)

它使调用者更容易不必传递一些参数。假设端口默认为80并且方法默认为“POST”,则只需要以下代码

http.request({
  hostname: 'www.google.com',
  path: '/upload',
}, callback)

如果切换到所有参数,请求函数将采用以下参数

http.request(hostname, port, path, method, callback);

在需要默认值的情况下,您需要像

一样调用它
http.request(hostname, undefined, path, undefined, callback);

你可以看到它看起来不太漂亮。我确实同意这种参数样式通常没有记录,如果没有详细记录,它可能会让人更难理解如何调用它

在提供类,如PHP,C#和Java的语言中,您可以创建一个类来表示JavaScript中的匿名对象。

在PHP中,您还可以使用数组来模仿JavaScript匿名对象。

答案 3 :(得分:0)

我看到这种模式的一些好处:

  1. 无需记住参数的顺序
  2. 更容易使一些(或所有)参数可选
  3. 在函数体内部,当你引用参数时,它是非常清楚的。例如options.user而不仅仅是user。这使得错误地覆盖传递的参数变得更加困难。