我发现c#中的命名参数特性在某些情况下非常有用。
calculateBMI(70, height: 175);
如果我想在JavaScript中使用这个,我可以使用什么?
我不想要的是:
myFunction({ param1: 70, param2: 175 });
function myFunction(params){
// Check if params is an object
// Check if the parameters I need are non-null
// Blah blah
}
这个方法我已经用过了。还有别的办法吗?
我可以使用任何库来做到这一点。
是的,嗯,有点。我找到了两个解决方案。我只解释一个。
在这个解决方案中,我们放弃了位置参数。
我们可以使用一个对象(几乎与Python中的dict相同)来传递参数。
在这个例子中,我使用函数来生成一个图像文件的名称:
// First we define our function with just ONE argument
function name_of_img(img_desc){
// With this step, any undefined value will be assigned a value
if(img_desc.size == undefined) {img_desc.size = "400x500"}
if(img_desc.format == undefined) {img_desc.format = ".png"}
console.log(img_desc.size + img_desc.format)
}
// Notice inside our function we're passing a dict/object
name_of_img({size: "200x250", format : ".jpg"})
// In Python name_of_img(size="200x250" , format="jpg")
// returns "200x250.jpg"
name_of_img({size: "1200x950"})
// In Python name_of_img(size="1200x950")
// returns "1200x950.png"
我们可以修改这个例子,所以我们也可以使用位置参数,我们也可以修改它,所以非有效参数可以传递,我想我会做一个关于这个的GitHub存储库。
与通常认为的相反,命名参数可以在标准的旧式JavaScript中(仅用于布尔参数)通过简单、整洁的编码约定实现,如下所示。
function f(p1=true, p2=false) {
...
}
f(!!"p1"==false, !!"p2"==true); // call f(p1=false, p2=true)
警告:
必须保留参数的顺序——但这种模式仍然有用,因为它可以很明显地显示哪个实际参数对应哪个形式参数,而不必为函数签名进行grep或使用IDE。
这只适用于布尔值。但是,我相信可以使用JavaScript独特的类型强制语义为其他类型开发类似的模式。
另一种方法是使用一个合适对象的属性,例如:
function plus(a,b) { return a+b; };
Plus = { a: function(x) { return { b: function(y) { return plus(x,y) }}},
b: function(y) { return { a: function(x) { return plus(x,y) }}}};
sum = Plus.a(3).b(5);
当然,对于这个虚构的例子来说,它是没有意义的。但在函数看起来像这样的情况下
do_something(some_connection_handle, some_context_parameter, some_value)
它可能更有用。它还可以与“parameterfy”思想相结合,以通用的方式从现有函数中创建这样一个对象。也就是说,它将为每个形参创建一个成员,该成员可以求值为函数的部分求值版本。
这个想法当然与Schönfinkeling也就是curiling有关。
调用函数f,将命名参数作为对象传递
o = {height: 1, width: 5, ...}
基本上是调用它的组合f(…g(o)),其中我使用蔓延语法和g是一个“绑定”映射连接对象值及其参数位置。
绑定映射正是缺失的元素,可以用它的键数组表示:
// map 'height' to the first and 'width' to the second param
binding = ['height', 'width']
// take binding and arg object and return aray of args
withNamed = (bnd, o) => bnd.map(param => o[param])
// call f with named args via binding
f(...withNamed(binding, {hight: 1, width: 5}))
请注意三个已解耦的成分:函数、带有命名参数的对象和绑定。这种解耦为使用此构造提供了很大的灵活性,其中绑定可以在函数的定义中任意定制,并在函数调用时任意扩展。
例如,在函数的定义中,你可能想要将高度和宽度缩写为h和w,使其更简短和清晰,同时你仍然想要使用全名来调用它:
// use short params
f = (h, w) => ...
// modify f to be called with named args
ff = o => f(...withNamed(['height', 'width'], o))
// now call with real more descriptive names
ff({height: 1, width: 5})
这种灵活性对于函数式编程也更有用,在函数式编程中,可以任意转换函数,而丢失其原始参数名。