Here are just a few tips I've picked up over the years to get some extra value out of these libraries.
Boolean optional parameters
- 0 (zero)
- "" (empty string)
- NaN (a special Number value meaning Not-a-Number!)
Because of this, is makes it really tricky to check for boolean false arguments. You can't just do this:
Underscore comes with a ton of utility functions for quickly checking if a value is a particular type. In our particular case, the isUndefined method comes in super handy and helps us make for some easy and expressive code to do what we want.
.tap > temporary vars
Temporary variables are ugly… there I said it. They make for code that isn't very friendly to read and are completely unexpressive. Coming from the Ruby world, I tend to try to write tiny methods that are as clean and expressive as possible. It's a great habit to be in, and one of the best things I've pulled from my time with Ruby.
So, let's say we have a method where we get a value from some other method, we make a slight modification to it, run it through a 3rd method and then return it.
In most cases, it would probably look something like this.
However, you can make this a lot cleaner by not using temporary variables and instead using underscore's .tap method.
Tap is a method who's purpose was originally created so that you could "tap" into objects in the middle of a long method chain and make modifications to them. But, I've found it's great for losing those ugly temporary variables and making code more expressive.
Of course, this is even better if you're using the chaining methods (which we'll discuss a few paragraphs down), but it's great even in this simple case.
If you come from the Ruby and Rails world, you probably have gotten into the habit of passing methods a hash/object of options as a parameter to your methods. This allows you to basically have a limitless number of arguments in your methods without having to constantly update your method signatures.
However, this can become quickly messy. The naïve approach is to just check to see if the parameter exists, but of course, since the whole params object could be undefined as well, you further have to check for that. It would probably look something like this.
We can clean this code up a bit by checking the params object at the top of the method. This way we can rely on all the keys in the params object being available and set to some reasonable default value. However, it still looks kind of messy.
This code doesn't really show show off how painful this type of boilerplate gets since there are only 3 keys represented. But imagine the params object has 20 different options? Or what if the params are actually inherited from something else and you just are adding a few more?
This is where underscore's defaults method comes in incredibly handy. It allows us to pass an object of what we think params should be, and then it will fill in any missing keys with those default values. It's an incredibly clean solution.
And the best part, if you have multiple methods that require the same (or even mostly the same) set of params keys, you can define that defaults object somewhere in your class and just pass it in.
I use this ALL the time.
One of the things I learned from Ruby is the idea of chaining together a bunch of functional type methods. it's a beautiful workflow that allows us to not have a bunch of ugly temporary variables hanging around to do the equivalent of one unit of work.
Underscore allows us to the do the same sort of thing with most of it's methods. Instead of passing the same temporary variable to 5 different methods, you do can do it all in one expressive shot.
For a really goofy example, let's say we needed to have a method that does the following:
- Take an array of words
- Append 123 to the end of each word
- Capitalize the words
- Reverse the order
- And finally, join the words together with spaces.
And this code is fine. It works and it's fairly safe from errors. But it's incredibly unexpressive and hard to read. Everything is done out of order from my list of functionality above.
Now that is some expressive code and is super easy to read. It does everything in the order that we laid out above and it's clear what exactly it's doing.
The chain method basically take the value that's passed to it and puts it into a wrapper object. That wrapper object then has access to most of the underscore methods as they're mixed into it. When the method is called on the wrapped object, the value it's actually wrapping is passed as the first argument automatically and then replaces the wrapped object with whatever the method returns.
In this case, value is returning back our modified array of string objects. Since it's just a regular array at this point, we just call join on it to make it one string and return it.
It's an incredibly elegant way to program and reminds me of a Lisp-type language, or at the very least, Ruby. This is a good thing.
Add your own methods (and chain them!)
Because Underscore doesn't come with just enough awesome stuff on it's own, it also allows to add your own methods to it. The provided mixin method allows you to add methods directly Underscore and make them as important as the map or each methods.
At the very minimum, it allows you to not pollute the global namespace with a bunch of utility methods. Underscore is going to be available to all your code anyway, so you might as well make it as a common place to include your utility methods that you use throughout.
Let's say we want to write our goofy method above for handling strings as a Underscore mixin, because we use it in a ton of different places in our app. It would look something like this.
And that's all well and good. But, in reality, it's not all that terribly useful.
The beauty of mixin, however, is the ability to add custom methods that are chainable. For the most part, you get this functionality for free. If your method is in a chain, it will receive the wrapped value as the first argument. And just as any other chained method, it will take what's returned from it and replace the wrapped value with the new value.
Below is the same code using a chain. However, I made one slight adjust to show how it works seamlessly with Underscore's native chained methods. In this case, I'm going to return just the first word of the array using Underscore's first method.
Once you get the hang of this, you start finding all kinds of incredible uses. Processing arrays of data even with advanced custom functionality becomes almost mindless. It will also become a common workflow that you will learn to depend on and exploit as much as possible.