Underscore.js and ES6
Published: 2024-02-13 10:35:44

**Underscore.js** is a JavaScript library that provides a whole mess of useful functional programming helpers without extending any built-in objects. It was first released in 2009 by Jeremy Ashkenas, who is also known for creating Backbone.js and CoffeeScript. Underscore.js offers utility functions for common programming tasks, including manipulating arrays, objects, and functions.

As for its current usage, while Underscore.js was extremely popular in the early 2010s for its utility functions, the landscape of JavaScript has evolved significantly since then. Many features that were once only available through libraries like Underscore.js have been incorporated into the ECMAScript standard, making these libraries less essential for modern development. For example, ES6 (ECMAScript 2015) introduced a slew of new features, including arrow functions, promises, and template literals, as well as new methods for arrays and objects that replicate much of the functionality provided by Underscore.js.

Today, while Underscore.js is not as widely used as it once was, it remains a valuable resource for projects that require compatibility with older browsers or that already have a dependency on it. Developers might also prefer it for its concise API and functional programming style in specific scenarios. However, for most modern JavaScript development, native ES6+ features and other libraries or frameworks like Lodash (a similar library that offers more features and better performance in some cases) or functional programming libraries such as Ramda may be more commonly recommended.


Let's explore a comprehensive list of Underscore.js functionalities that have been integrated into ES6 (ECMAScript 2015) and beyond, providing examples of both Underscore.js and their native JavaScript counterparts.

## Only in Underscore.js
### 1. bindAll
The bindAll function in Underscore.js is used to permanently bind one or more methods of an object to the object itself. This is particularly useful for ensuring that the this context within methods accurately points to the object, especially when methods are passed as callbacks or used as event handlers. By using bindAll, you can ensure that this within the methods always refers to the specified object, preventing this from referring to a different context.
```js
_.bindAll(object, methodNames...)
```
object: The object whose methods' this context you want to bind.
methodNames: One or more method names to bind. Each name is specified as a string. You can list multiple method names separated by commas.
```js
var obj = {
  name: 'Underscore.js',
  greet: function() {
    console.log("Hello from " + this.name);
  },
  farewell: function() {
    console.log("Goodbye from " + this.name);
  }
};

// Bind the greet and farewell methods to the obj object
_.bindAll(obj, 'greet', 'farewell');

setTimeout(obj.greet, 1000); // Outputs "Hello from Underscore.js"
setTimeout(obj.farewell, 1000); // Outputs "Goodbye from Underscore.js"
```
In this example, when obj.greet and obj.farewell are called using setTimeout, _.bindAll ensures these methods are correctly bound to the obj object, allowing this.name to correctly reference the object's name property.

More : [bindAll](https://underscorejs.org/#bindAll)

### 2. wrap
The purpose of wrap is to take an existing function and a wrapper function, and return a new function that, when called, calls the wrapper function with the original function passed as the first argument, followed by any arguments passed to the new function.

Syntax:
```js
_.wrap(function, wrapper)
```
function: The original function to wrap.
wrapper: The function that will wrap the original function. The first argument to the wrapper will be the original function.

**Example:**
```js
var greet = function(name) { return "hello: " + name; };
var shout = _.wrap(greet, function(func) {
  return func().toUpperCase() + "!";
});

shout('moe');
// => "HELLO: MOE!"
```
In this example, shout is a new function that wraps greet with additional functionality (converting the greeting to uppercase and appending an exclamation mark).

More : [wrap](https://underscorejs.org/#wrap)

### 3. _.mixin(object)
Allows you to extend Underscore with your own utility functions. Pass a hash of {name: function} definitions to have your functions added to the Underscore object, as well as the OOP wrapper. Returns the Underscore object to facilitate chaining.
```js
_.mixin({
  capitalize: function(string) {
    return string.charAt(0).toUpperCase() + string.substring(1).toLowerCase();
  }
});
_("fabio").capitalize();
=> "Fabio"
```
### 4. _.template
Compiles JavaScript templates into functions that can be evaluated for rendering. Useful for rendering complicated bits of HTML from JSON data sources. Template functions can both interpolate values, using <%= … %>, as well as execute arbitrary JavaScript code, with <% … %>. If you wish to interpolate a value, and have it be HTML-escaped, use <%- … %>. When you evaluate a template function, pass in a data object that has properties corresponding to the template's free variables. The settings argument should be a hash containing any _.templateSettings that should be overridden.
```js
var compiled = _.template("hello: <%= name %>");
compiled({name: 'moe'});
=> "hello: moe"

var template = _.template("<b><%- value %></b>");
template({value: '<script>'});
=> "<b>&lt;script&gt;</b>"
```
You can also use print from within JavaScript code. This is sometimes more convenient than using <%= ... %>.
```js
var compiled = _.template("<% print('Hello ' + epithet); %>");
compiled({epithet: "stooge"});
=> "Hello stooge"
```

## Similar Funktion in Underscore.js and ES6

_.isUndefined
Returns true if value is undefined.
```js
_.isUndefined(window.missingVariable);
=> true
```
_.keys
Retrieve all the names of the object's own enumerable properties.
```js
_.keys({one: 1, two: 2, three: 3});
=> ["one", "two", "three"]
```
_.isFunction
_.find
_.has
_.defaults
_.isEmpty
_.last
_.first
_.isArray
_.size
_.each
_.rtoken
_.set

### Array Methods

- _.each / _.forEach
```
Underscore.js: _.each([1, 2, 3], alert);
ES6: [1, 2, 3].forEach(alert);
```
- _.map

```
Underscore.js: _.map([1, 2, 3], function(num) { return num * 3; });
ES6: [1, 2, 3].map(num => num * 3);
```
- _.filter

```
Underscore.js: _.filter([1, 2, 3, 4, 5], function(num) { return num % 2 === 0; });
ES6: [1, 2, 3, 4, 5].filter(num => num % 2 === 0);
```
- _.find

```
Underscore.js: _.find([1, 2, 3, 4, 5], function(num) { return num % 2 === 0; });
ES6: [1, 2, 3, 4, 5].find(num => num % 2 === 0);
```
- _.reduce

```
Underscore.js: _.reduce([1, 2, 3], function(memo, num) { return memo + num; }, 0);
ES6: [1, 2, 3].reduce((memo, num) => memo + num, 0);

```
- _.every

```
Underscore.js: _.every([2, 4, 6], function(num) { return num % 2 === 0; });
ES6: [2, 4, 6].every(num => num % 2 === 0);
```
- _.some

```
Underscore.js: _.some([2, 4, 5], function(num) { return num % 2 === 0; });
ES6: [2, 4, 5].some(num => num % 2 === 0);
```


### Object Methods
- _.keys

```
Underscore.js: _.keys({one: 1, two: 2, three: 3});
ES6: Object.keys({one: 1, two: 2, three: 3});
```
- _.values

```
Underscore.js: _.values({one: 1, two: 2, three: 3});
ES6: Object.values({one: 1, two: 2, three: 3});
```
- _.extend / Object.assign

```
Underscore.js: _.extend({name: 'Moe'}, {age: 50});
ES6: Object.assign({name: 'Moe'}, {age: 50});
```


### Utility Methods
- _.range

```
Underscore.js: _.range(10);
ES6: Array.from({length: 10}, (v, k) => k);
Alternative ES6: [...Array(10).keys()];
```


### Function Methods
While ES6 does not directly replace all function utilities from Underscore.js like _.debounce or _.throttle, newer JavaScript environments and libraries often provide similar functionalities, and developers can implement these patterns using native JavaScript or newer web APIs.


### More Informations: [You-Dont-Need-Lodash-Underscore](https://github.com/you-dont-need/You-Dont-Need-Lodash-Underscore)