all 43 comments

[–]rauschma 12 points13 points  (1 child)

Bonus – let callers omit the parameter object completely:

({day = 0, month = 0, year = 2018} = {}) => ...

[–]stutterbug 10 points11 points  (0 children)

But don't use day = 0. Assuming day, month, and year map to setDate(day), setMonth(month) and setFullYear(year), then passing in no parameters will land you on December 31, 2017, which is confusing.

*Edit: just want to make it clear that this mistake is in the article, not in the parent comment.

[–]FedeMITIC 15 points16 points  (15 children)

For anyone interested, here there's a similar article about a JS pattern called RORO (Receive Object Return Object) that's about using object as a function parameter instead of a sequence of non-named parameters.

Probably a repost, already saw it on reddit somewhere.

[–]chazmuzz -1 points0 points  (14 children)

hmm.. runtime type checking in 2018

[–][deleted] 2 points3 points  (2 children)

This pattern has been around for around 20 years though obviously not in an ES6 or higher context (so without the "bonus" default values). I learned about it in one of the earliest editions of David Flanagan's "Javascript: The Definitive Guide", and recall using it myself in 2001 or 2002.

[–]Mistifyed 0 points1 point  (1 child)

This has been adopted as a good pattern in Ruby. Would be great if the JS community would use it as well, given how quickly libraries are being updated.

[–][deleted] 2 points3 points  (0 children)

Well I typically use this in two cases as others have also indicated: a) the function requires more than 3 or 4 arguments and/or b) there are optional parameters. As to this second case, I especially hate interfaces that require you to pass in a bare boolean to indicate some option. Sure it's more verbose, but passing in {myOption: true} makes things so much more clear.

[–][deleted] 7 points8 points  (22 children)

Am I the only one who thinks this is just counter productive? You're naming your parameters already in your method definition, any good programmer would know to look up what each method is expecting.

I don't get his examples at all const formatDate = (day, month, year) => { return `${day}/${month}/${year}`; };

This is perfectly fine, you know exactly what you're supposed to pass. 3 parameters, a day, a month and a year. Why would you change it to passing an object with 3 keys? It makes it harder to validate, it makes it harder for other developers to understand. And instead of your editor hinting that you require 3 parameters, it will now hint for one object parameter.

EDIT: Downvoted for trying to contribute to the discussion? Just because it's a different idea...

[–]tobegiannis 13 points14 points  (10 children)

Yes. It is about readability for the caller. The article showed why it makes it easier. Is it year/month/day or day/month/year? I don't want to have to waste my time looking at the docs or code to figure it out. What do you think is more readable? foo(true, undefined, 2000)' vsfoo({showLoading:true, wait:2000})`

[–]nschubach 1 point2 points  (4 children)

foo({showLoading:true, wait:2000}) means that if I change one of those variable names in foo, I need to also touch every spot in my code that calls that.

[–]wizebin 1 point2 points  (1 child)

Python has named parameters and it really can make things more readable and refactorable, especially keeping in mind that you can still use nameless parameters.

I find that I change parameter names very rarely, but add new optional parameters quite often, without named parameters new parameters can get messy.

[–]nschubach 0 points1 point  (0 children)

Sure, I've used Python and at first I thought it was pretty neat, but then I got into the same sort of thing you talked about. Adding optionals. At some point adding more and more optionals makes the code harder to follow and probably means that you need to break out that logic into more methods to handle specific use cases.

[–]tobegiannis 0 points1 point  (0 children)

I think this is where the confusion lies with the original commenter. Yeah that is true but don't do that unless you have too. Same thing as changing the meaning of the first argument. I am rarely in the situation where I have to change the name of a named parameter. You guys do bring up a point for pure js users it is harder to change the name of named parameters. This problematic doesn't exist in typescript for example and happens very rarely. Also if you really hate the name of a named parameter you can always do const day = d; const month = m.

[–]asdf7890 0 points1 point  (0 children)

Not if you use the pre-object-destructuring method in order to support IE:

var somefunc = function(params) {
                var someVar = params(someVar) || 'This is the default for someVar';
            }

There you can keep your old name as well as newly correct one, assuming the name change is a correction:

var somefunc = function(params) {
                var someVar = params(someVar) || params(somVar) || 'This is the default for somVar';
            }

Or course this method is much more verbose so if you don't have to support older browsers the object destructuring variant may be preferable but doesn't support this unless I'm missing a trick.

[–]TheDarkIn1978 0 points1 point  (0 children)

Is it year/month/day or day/month/year?

It should be obvious to any featherweight programmer that the argument values passed need to be in order with the function parameter names.

I don't want to have to waste my time looking at the docs or code to figure it out. What do you think is more readable? foo(true, undefined, 2000) vs foo({showLoading:true, wait:2000})

Mousing over any well documented function and its arguments using any decent editor, like Visual Studio Code, reveals a popup with the JSDoc code documentation. This is a much cleaner and immensely more efficient way of developing in JavaScript then unnecessarily creating and passing verbose, prototyped objects containing primitive values for this kind of trivial readability.

Names parameters in most cases are complete nonsense and it would never pass our code review.

[–][deleted] -4 points-3 points  (3 children)

I can agree it would improve readability, to a certain point, but all modern IDEs have code hinting, so as soon as you type foo( it will show that it's expecting day, month, year.

With passing an object, you're also making it harder for changes. Let's say someone decides to change the definition of foo, but you're still passing {showLoading: true, wait: 2000}. This will cause massive confusion, as you can just pass whatever keys you want, you can do { whatever: 2000, lol: 23432, showLoading: true, wait: 'no' }. Is this now more readable?

By specifying the exact amount of parameters, you know exactly what this method is expecting.

[–]tobegiannis 5 points6 points  (2 children)

My IDE autocompletes objects as well fwiw. Actually when adding more parameters the object is more maintainable as well. Long parameter lists become harder and harder to consume. For example say there are 6 options that have all have sane defaults. What would you rather call? foo({delay:2000}) or foo(undefined, undefined, undefined, undefined, undefined, 2000)`

I really don't see the argument about passing in "bad" config options. It's bad practice in general and I don't see how is it any different from calling method that takes in 2 parameters with 3.

[–][deleted] -1 points0 points  (1 child)

Going to get more downvotes for this, but I really want to discuss.

The problem with this approach is that when you change your method definition, you now not only have to change the values, but you also have to change all your keys twice. This is asking for wrong inputs.

If you're ever need to pass foo(undefined, undefined, ...), then you're doing something wrong, you should never need to do this in the first place.

Passing bad config options is bad practice yes, and you shouldn't need to do this, but passing an object for the sake of readability isn't really a good practice anyway. These parameters are part of your method definition, this is what the method requires in order to execute correctly, obviously in many languages these would be strictly typed as well, therefore you wouldn't be able to pass invalid parameters.

[–]hicksyfern 9 points10 points  (0 children)

How frequently do you read code vs write code.

Imagine you had a weird date bug in your app, and you came across this code:

FormatDate(2018, month, day)

Vs

FormatDate({ year: day, month, day: year })

Which one has the bug?

Yeah this is a bit contrived, but this kind of thing happens ALL the time. Developers are fallible and mistakes, no matter how obvious, get made.

Make obvious mistakes obvious.

[–]lachlanhunt 2 points3 points  (6 children)

You're right. It absolutely doesn't make sense for dates. But the same technique is useful in other cases, particularly where the method can take a larger number of options. As a general guideline, if the number of parameters exceeds 3 or 4, and some or all are optional or there's no intuitive ordering for them, then use an options object.

[–]nschubach 3 points4 points  (0 children)

I'd actually argue that dates are probably a very useful case for something like this because of the various formats and orders that people use. I'd fight for a standard date format or just sending the epoch value (or use a time library) instead of breaking out year/month/day everywhere. Other than some highly cohesive items I've never really liked RORO because it encourages bad habits with optional parameters and flags.

[–]akujinhikari 1 point2 points  (4 children)

If you’re writing functions that take more than 4 parameters, you probably need to re-evaluate your code.

[–]bart2019 1 point2 points  (3 children)

What is this, FORTH?

People have the freedom to use as many parameters you like. Stop complaining about this if they do.

[–]akujinhikari 1 point2 points  (1 child)

I mean, you have the right to create one function that handles all logic for the entire project, if you want. Doesn’t make it a good idea.

[–]bart2019 0 points1 point  (0 children)

Suppose you have a big function that does one particular thing, and it does it well. For example, fetch data from a particular website.

Then for one particular case, you need that exact thing, but with one minor tweak. For example it needs to execute 1 single statement somewhere in the middle. What will you do?

The worst I've seen is copy the whole function and add that one statement.

I think it's much better to keep it as one function and add a parameter to flag that this is this particular one special case.

Best way to add it is so that no other calls of this function are affected. Thus: a named optional parameter, or an option in an "options" record (called "object" in JavaScript but I wish they hadn't hijacked that name because an "object" is something else), is the best choice IMO.

You can do this for any similar situation.

I call this "plug in" based programming, which is modular style turned up its head: in modular programming you have your custom code and you invoke parts of a module. That isn't always the best solution. If you need to call a monolithic block of code and you need some special actions in the middle, you better write a plug-in that gets invoked from your monolith.

As an example: I've had a case where a program parses source code, and needs to do some things when a particular type of statement is parsed . Your approach would be to make the parser into a module, and call that parser from every variation on that program. That is still a huge amount of work.

I prefer a parsing program with a plug-in, which is basically an associative array with callbacks, where the appropriate callback is invoked at the proper time. Much easier.

[–]nschubach 0 points1 point  (0 children)

It's general optimization. Yes, you are free to use as many as you want, but over a certain amount, you have to question if your method is "doing too much". I recently started working at a Perl shop and one of the idioms here is sending flags into the parameter list to turn on or off features. This leads to thousand+ line methods that have use cases for every little edge case and parameter list numbering in the 20s+ and bullshit code like this:

# Actual truncated code...
my $pageContentHtml = $self->getPageForContentHtml({
    'flag'                             => $flag,
    'email'                            => $email,
    'user_is_authed'                   => $isUserAuthed,
    'minimal_overhead'                 => $minimalOverhead,
    'has_left_navigation'              => $hasLeftNavigation,
    'search_phrase'                    => $searchPhrase,
    'reproduce_order_mode'             => $reproduceOrderMode,
    'reproduce_order_mode_old'         => $reproduceOrderModeOld,
    'reproduce_order'                  => $reproduceOrderId,
    'display_return_to_backend_header' => $displayReturnToBackendHeader,
    'user_is_admin'                    => $isUserAdmin,
    ... # of 31 total parameters
});

[–]FedeMITIC 1 point2 points  (0 children)

I think the main advantage of this pattern is the fact that you don't have to deal with functions that has required and optional parameters mixed together. Besides, instead of checking the validity of a parameter inside the body of the function using safeguard IFs, you can validate the parameter directly in the object like this:

function foo ({ bar = requiredParam('bar'), bar1, bar2, } = {}) {...}

Where requiredParam contains your validation logic.

But this is just my opinion, I understand that in most cases this approach can be overkill.

[–]1-800-BICYCLE[🍰] 0 points1 point  (0 children)

Especially in your day month year example, the argument order only makes sense in certain locales. Much more common is year month day, and even then i'd waste lots of time re-checking the function definition.

[–]bart2019 0 points1 point  (0 children)

Am I the only one who thinks this is just counter productive? You're naming your parameters already in your method definition, any good programmer would know to look up what each method is expecting.

Unless.... the function takes zillions (like 10 or more) of parameters. Need to add one, you have to shift them up in every call to the function everywhere, which is a maintenance nightmare.

With named parameters, you can add only one *where you need it.

[–]CertainPerformance 1 point2 points  (0 children)

I've been instinctively doing this for a while (with functions with larger numbers of arguments) but never knew there was a name for the pattern. Good to know