JavaScript:为什么命名参数比位置参数更好

By passing arguments as objects, we can decrease bugs in our app. That’s always an important thing but even more so when sharing reusable functions/components between apps (for example, using tools like Bit).

通过将参数作为对象传递,我们可以减少应用程序中的错误。 这始终是一件重要的事情,但在应用之间共享可重用功能/组件时(例如,使用Bit之类的工具),则更是如此。

This approach is called named arguments. Read the full article to understand why its better.

这种方法称为命名参数。 阅读全文,以了解它为什么更好。

Before talking about named arguments let’s first clearly understand positional arguments and the problems they can cause.

在讨论命名参数之前,我们首先要清楚地理解位置参数及其可能引起的问题。

什么是位置参数? (What are positional arguments?)

You must be pretty familiar with positional arguments even if you heard the name for the first time.

即使您是第一次听到名称,您也必须非常熟悉位置参数。

Normal functions using positional arguments
使用位置参数的普通函数

The greet function takes two arguments- firstName & lastName. The caller has to make sure that the firstName is the first argument and the lastName is the second argument. The important takeaway here is that the name of the argument doesn’t have any significance. The only thing that matters is the order in which the arguments are passed.

greet函数接受两个参数-firstName和lastName。 调用者必须确保firstName是第一个参数,lastName是第二个参数。 这里重要的一点是, 论点名称没有任何意义 。 唯一重要的是参数传递的顺序

This familiar approach is called positional arguments. It is usually fine for cases where you pass one or two arguments since its hard to mess up the order of arguments. But if you have to call a util which takes 6 arguments it would be hard to remember the order of arguments to pass. You don’t want to pass the password in place of the username argument.

这种熟悉的方法称为位置参数。 通常,在您传递一个或两个参数的情况下,这很好,因为它很难弄乱参数的顺序。 但是,如果必须调用带有6个参数的util,则很难记住要传递的参数顺序。 您不想通过密码代替username参数。

位置参数问题 (Problems with Positional Arguments)

Positional Arguments are pretty straight-forward to do but you will face some challenges with them.

位置参数很简单,但是您将面临一些挑战。

1.不能跳过中间参数 (1. Can’t skip middle arguments)

Say you have changed the greet function from earlier such that it takes 3 arguments now — firstName, middleName, lastName. Since many people don’t have a middle name you want to make middleName an optional argument. The only way to call greet function with only firstName and lastName is this.

假设您已经更改了greet函数,使其现在需要3个参数-firstName,middleName和lastName。 由于许多人没有中间名,因此您希望将MiddleName设为可选参数。 仅使用firstName和lastName调用greet函数的唯一方法是此方法。

greet('Aditya', null, 'Agarwal');
// Correct ✅greet('Aditya', 'Agarwal');
// Incorrect ❌

You can’t just provide firstName and lastName. This problem becomes more pronounced when the number of optional arguments increases to let’s say 5. Now you have to provide 5 nulls just to be able to provide arguments after those.

您不能只提供名字和姓氏。 当可选参数的数量增加到5个时,这个问题变得更加明显。现在,您必须提供5个null才能在这些参数之后提供参数。

2.将类型添加到位置参数不太干净 (2. Adding types to positional arguments is less cleaner)

Nowadays adding types to your utilities is becoming very common. With positional arguments you have no choice but to inline the types along with the function definition. This can obfuscate the code a little. It would be much better if we could declare type definitions of all arguments in one block.

如今,向您的实用程序添加类型已变得非常普遍。 使用位置参数,您别无选择,只能将类型与函数定义一起内联。 这会使代码有些混乱。 如果可以在一个块中声明所有参数的类型定义,那就更好了。

I don’t want to cover Types in this article. You can bug my friend Gurjit to cover this in his articles as he’s the one who pointed this out to me.

我不想在本文中介绍类型。 您可以让我的朋友古尔吉特(Gurjit)在他的文章中加以介绍,因为他是向我指出这一点的人。

3.引起细微的错误 (3. Cause subtle bugs)

Positional Arguments pack a lot of implicit behavior which can be the cause of subtle bugs. Let's see a common JS trick question

位置参数包含许多隐式行为,这可能是导致细微错误的原因。 让我们看一个常见的JS技巧问题

Tricky JavaScript question
棘手JavaScript问题

Surprised? The reason for this weird output is hidden behind the implicit nature of positional arguments. You see the map and parseInt functions are hiding some of their secrets in plain sight.

惊讶吗 这种奇怪输出的原因隐藏在位置参数的隐含本质之后。 您会看到map和parseInt函数在显而易见的情况下隐藏了它们的一些秘密。

Let’s review the code numbers.map(parseInt) again.

让我们再次查看代码numbers.map(parseInt)

What exactly is happening here?

这里到底发生了什么?

  • We run the map function on the numbers array.

    我们在numbers数组上运行map函数。
  • map takes the first item of the array and passes it to parseInt.

    map获取数组的第一项并将其传递给parseInt。
  • Now, for the first item in the array (i.e. 1) it would do parseInt(1). Right...? Wrong!!!

    现在,对于数组中的第一项(即1),它将执行parseInt(1) 。 对...? 错误!!!

Actually map passes three arguments to its callback function. The first is the current item in the array, the second is the index of the item. The third is the entire array. This in itself has no problem but the real issue is with the latter part.

实际上,map将三个参数传递给其回调函数。 第一个是数组中的当前项目,第二个是项目的索引。 第三个是整个数组。 这本身没有问题,但真正的问题在于后一部分。

numbers.map(parseInt) is not the same as numbers.map((item) => parseInt(item)). You could make the assumption that since the callback function just takes the item argument and pass it to parseInt we can skip the additional step. But the two are different: in the former, we pass all the data from map to parseInt whereas in the latter we only pass the item.

numbers.map(parseInt)numbers.map((item) => parseInt(item)) 。 您可以假设,由于回调函数仅接受item参数并将其传递给parseInt,因此我们可以跳过附加步骤。 但是两者是不同的:在前者中,我们将所有数据从map传递到parseInt,而在后者中,我们仅传递项。

You might not know but there is a second argument of parseInt called the radix. By default, the value of radix is 10 (base 10 because we humans follow the decimal system for counting). What went wrong with the code was that we passed the index of the current item as the radix value to parseInt. These are the actual function calls that happened-

您可能不知道,但是parseInt的第二个参数称为基数。 默认情况下,基数的值为10(以10为底,因为人类遵循十进制进行计数)。 代码出问题的地方是,我们将当前项目索引作为基数值传递给parseInt 。 这些是发生的实际函数调用-

parseInt('1', 0, [...]);parseInt('4', 1, [...]);parseInt('8', 2, [...]);parseInt('10', 3, [...]);

Now that we know the issues, how can we do better?

现在我们知道了问题,我们如何才能做得更好?

位置参数的替代 (Alternative to Positional Arguments)

Image for post

What if a function could tell by the name, what arguments it expects? That way even if you pass extra data to it by mistake it will only use the things it needs.

如果一个函数可以通过名称告诉它,它期望什么参数呢? 这样,即使您错误地将多余的数据传递给它,它也只会使用它需要的东西。

Let’s make our own wrapper over parseInt. Here’s a naive implementation.

让我们对parseInt进行包装。 这是一个幼稚的实现。

Code for myCustomParseInt
myCustomParseInt的代码

myCustomParseInt accepts only one argument and that is an object. This object can have two keys– item & radix. Let’s use our custom util with map. It will be necessary to have an intermediate step to send args received by callback to myCustomParseInt.

myCustomParseInt仅接受一个参数,它是一个对象。 该对象可以具有两个键-项目和基数。 让我们将自定义工具与map一起使用。 必须有一个中间步骤,将回调收到的args发送到myCustomParseInt。

Using myCustomParseInt with map
将myCustomParseInt与地图一起使用

Notice that even if we pass the index to myCustomParseInt it won’t cause any problems. That’s because myCustomParseInt will just ignore it. This pattern of passing objects to functions is called named arguments. It is lot more explicit than positional arguments.

请注意,即使我们将索引传递给myCustomParseInt也不会造成任何问题。 那是因为myCustomParseInt只会忽略它。 将对象传递给函数的这种模式称为命名参数 。 它比位置参数更明确。

To change the radix we have to explicitly pass the radix key. That means if we want to parse a string with base 2 we have to go to the docs and see the exact name of parameter (radix). If we blindly pass any other key it won’t do anything. This is great for us because it avoids unintended behavior.

要更改基数,我们必须显式传递基数键。 这意味着如果要解析以2为底的字符串,则必须转到文档并查看参数(基数)的确切名称。 如果我们盲目地传递任何其他键,它将不会执行任何操作。 这对我们来说很棒,因为它避免了意外行为。

具有解构的命名参数 (Named arguments with destructuring)

A while back JavaScript got a feature called destructuring. Let’s use this in myCustomParseInt implementation.

不久前,JavaScript获得了称为解构的功能。 让我们在myCustomParseInt实现中使用它。

Named arguments with destructuring
具有解构的命名参数

You’ll notice that just by adding two curly braces we get the benefits of named args but the ergonomics of positional args. You can think of destructuring as performing const item = objArgs.item;

您会注意到,仅通过添加两个花括号就可以得到命名args的好处,但可以得到位置args的人体工程学。 您可以将销毁视为执行const item = objArgs.item

If myCustomParseInt is called with undefined then JS would throw an error. That’s because undefined.item is not allowed. To avoid it we can add = {} in the end of destructuring. That way when we pass undefined it will now do {}.item which is valid JS. Here's the final implementation-

如果使用未定义调用myCustomParseInt,则JS将引发错误。 那是因为不允许undefined.item 。 为了避免这种情况,我们可以在解构结束时添加= {} 。 这样,当我们传递undefined时,它将执行{}.item这是有效的JS。 这是最终的实现-

Final Implementation
最终实施

With named arguments pattern we can also skip the arguments we don’t want to provide since the function no longer depends on the order in which the arguments are passed.

使用命名参数模式,我们也可以跳过不想提供的参数,因为该函数不再取决于参数传递的顺序。

Named Args pattern make it easy to skip optional args
命名为Args模式,可以轻松跳过可选的args

In conclusion I’d say named arguments is a powerful pattern and it has become very commonplace nowadays but you don’t always need to use them. Sometimes you could even combine the two. The fetch API in browser is used like this

总而言之,我想说命名​​参数是一种强大的模式,如今它已变得非常普遍,但是您不必总是使用它们。 有时您甚至可以将两者结合在一起。 浏览器中的提取API的用法如下:

Combine positional args with named args
将位置参数与命名参数组合

Here the mandatory argument (API path) is a positional argument and then the optional params are accepted through named arguments.

此处,强制参数(API路径)是位置参数,然后可选参数通过命名参数接受。

Follow me on Twitter (https://twitter.com/dev__adi) if you have questions or if you want to stay up-to-date with cool developer tips and interesting technologies.

如果您有疑问或想了解最新的开发人员技巧和有趣的技术, Twitter(https://twitter.com/dev__adi)上关注我。

学到更多 (Learn More)

翻译自: https://blog.bitsrc.io/javascript-why-named-arguments-are-better-than-positional-arguments-9b15ab3155ef

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值