Underscore.js
for President.
WAT.
"Underscore is a utility-belt library for JavaScript that provides a lot of the functional programming support that you would expect in Prototype.js (or Ruby), but without extending any of the built-in JavaScript objects."
Basically:
A really awesome utility belt.
"Namespaced" under _
That's an underscore.
Let's jump into it
Some code!
max / min
var someNumbers = [15, 88, 32, 64, 48, 16, 8];
console.log(_.max(someNumbers)) // 88
... or with an array of objects:
var coders = [
{ nick: 'HooverDam', age: 27 },
{ nick: 'tjoc', age: 88 },
{ nick: 'Voldern', age: 16 }
];
console.log(_.max(coders, function(coder) {
return coder.age;
}); // { nick: 'tjoc', age: 88 }
groupBy
var employees = [
{ name: 'Espen H', role: 'Coder' },
{ name: 'Hanne', role: 'Project leader' },
{ name: 'André', role: 'Coder' },
{ name: 'Audun', role: 'Sysop' },
{ name: 'Trygve', role: 'Sysop' },
{ name: 'Naveed', role: 'Coder' },
{ name: 'Espen V', role: 'Coder' },
{ name: 'Tommy', role: 'Suit' },
{ name: 'Stein', role: 'Sysop' },
{ name: 'Christer', role: 'Coder' }
];
_.groupBy(employees, 'role');
/* {
'Coder': [{}, {}, ...],
'Project leader': [{ name: 'Hanne', role: 'Project leader' }],
'Sysop': [{}, {}, ...],
'Suit': [{ name: 'Tommy', role: 'Suit'}]
} */
filter
var employees = [ { name: 'Espen H', role: 'Coder' }, { name: 'Hanne', role: 'Project leader' }, { name: 'André', role: 'Coder' }, ... { name: 'Tommy', role: 'Suit' }, { name: 'Stein', role: 'Sysop' }, { name: 'Christer', role: 'Coder' } ];
_.filter(employees, function(employee) { return employee.role == 'suit'; }); /* [{ name: 'Tommy', role: 'Suit'}] */
find
Same as filter, but returns first matching:
var employees = [
{ name: 'Espen H', role: 'Coder' },
{ name: 'Hanne', role: 'Project leader' },
{ name: 'André', role: 'Coder' },
...
{ name: 'Tommy', role: 'Suit' },
{ name: 'Stein', role: 'Sysop' },
{ name: 'Christer', role: 'Coder' }
];
_.find(employees, function(employee) {
return employee.role === 'Coder'
});
// { name: 'Espen H', role: 'Coder' }
contains
var suit = { name: 'Tommy', role: 'Suit' }
, coder = { name: 'André', role: 'Coder' }
, employees = [suit, coder];
_.contains(employees, suit); // true
However: references vs copies
var suit = { name: 'Tommy', role: 'Suit' };
var employees = [
{ name: 'André', role: 'Coder' },
{ name: 'Tommy', role: 'Suit' }
];
_.contains(employees, suit); // false
Works on objects too! (for values)
pluck
var employees = [
{ id: 1337, name: 'Espen H', role: 'Coder' },
{ id: 1338, name: 'Hanne', role: 'Project leader' },
{ id: 31337, name: 'André', role: 'Coder' },
{ id: 1341, name: 'Christer', role: 'Coder' }
];
_.pluck(employees, 'id'); // [1337, 1338, 31337, 1341]
shuffle
var numbers = [
1, 2, 4, 8, 16,
32, 64, 128, 256
];
_.shuffle(numbers); // [16, 1, 32, 4, 2, 8, 128, 256, 64]
compact
var undef, values = [
1337,
false,
null,
'',
undef,
parseInt('z'),
15
];
_.compact(values); // [1337, 15]
difference
var someNumbers = [8, 16, 32, 128];
var someOthers = [2, 4, 8, 16, 256, 512];
_.difference(someNumbers, someOthers); // [32, 128]
Note: Elements from first not present in subsequent
without
var suit = { name: 'Tommy', role: 'Suit' };
var employees = [
{ name: 'Hooverdam', role: 'Coder' },
suit,
{ name: 'André', role: 'Coder' }
];
var suitless = _.without(employees, suit);
/* [
{ name: 'Hooverdam', role: 'Coder' },
{ name: 'André', role: 'Coder' }
] */
uniq
var someNumbers = [8, 16, 32, 128];
var someOthers = [2, 4, 8, 16, 256, 512];
_.uniq(someNumbers, someOthers); // [8, 16, 32, 128];
Don't get bitten by object reference
var employees = [ { name: 'Tommy' }, { name: 'Espen' }, { name: 'André } ];
var señors = [ { name: 'André' } ];
_.uniq(employees, señors, function(person) { return person.name; });
_.uniq(employees, señors, JSON.stringify);
object
Array(s) => object
var roles = ['SysOp', 'Coder', 'Coder', 'Suit']
, employees = ['Roy', 'André', 'Christer', 'Tommy'];
_.object(roles, employees);
/* {
SysOp: 'Roy',
Coder: 'Christer',
Suit: 'Tommy'
} */
Last value overwrites first, on duplicates
bind
Better name than $.proxy ;-)
var someClass = {
bindInput: function() {
var boundHandler = _.bind(this.onInput, this);
$('input[type=number]').on('input', boundHandler);
},
onInput: function(e) {
if (e.target.valueAsNumber > 1337) {
this.onLargerThanLeet();
}
},
onLargerThanLeet: function() {
window.alert('WE ARE LARGER THAN LEET');
}
};
bindAll
Same as bind, but binds multiple
var buttonView = {
label : 'underscore',
onClick : function() { alert('clicked: ' + this.label); },
onHover : function() { console.log('hovering: ' + this.label); }
};
_.bindAll(buttonView, 'onClick', 'onHover');
$('.underscore-bind').on({
click: buttonView.onClick,
hover: buttonView.onHover
});
memoize
var md5page = function() {
return crypto.md5($('html').html());
};
var cachedMd5Page = _.memoize(md5page);
cachedMd5Page(); // 1 sec
cachedMd5Page(); // 0.000001 sec
defer
Defer invoking of function until call stack is clear
var xhrCache = {},
getFile = function(url, onComplete) {
if (xhrCache[url]) {
return _.defer(onComplete, xhrCache[url]);
}
$.xhr(url, function(res) {
xhrCache[url] = res;
onComplete(res);
});
});
throttle
var onScroll = function() {
console.log('We\'re at ' + window.scrollTop + 'px');
};
$(window).on('scroll', _.throttle(onScroll, 50));
See also: debounce
once
var initialize = _.once(function() {
$('.some-el').on('hover', someFunction);
});
initialize(); // Binds event
initialize(); // Does nothing
after
var responses = [];
var onAllComplete = _.after(2, function() {
// Combine responses somehow
});
var onComplete = function(res) {
responses.push(res);
onAllComplete();
};
$.xhr('twitter.com/vgtech', onComplete);
$.xhr('twitter.com/vgnett', onComplete);
keys / values
var employeesAge = {
'Hooverdam': 27,
'Voldern' : 16,
'tjoc' : 87,
};
_.keys(employeesAge); // ['Hooverdam', 'Voldern', 'tjoc']
_.values(employeesAge); // [27, 16, 87]
functions
_.functions(jQuery.fn); // Whole lotta functions.
defaults
var options = {
'token' : 'someToken',
'format': 'json'
};
_.defaults(options, {
'baseUrl' : 'http://www.vg.no/api',
'format' : 'xml'
});
/* options = {
"token": "someToken",
"format": "json",
"baseUrl": "http://www.vg.no/api"
} */
omit
var apiResponse = {
id: 1337,
title: 'VG wins award for most beautiful webpage on the interwebs',
description: 'Some really verbose description here that we could live without',
created: 1376560776,
_meta: {
commentsUrl: '...',
photoUrl: '...'
}
};
_.omit(apiResponse, [
'_meta',
'description'
]);
has
Object.prototype.debug = function() {
return JSON.stringify(this);
};
var myObj = { 'foo': 'bar' };
!!myObj.foo; // true
!!myObj.debug // true
_.has(myObj, 'foo'); // true
_.has(myObj, 'debug'); // false
isEqual
var tjoc = { name: 'Tommy', role: 'Suit' };
var suit = { name: 'Tommy', role: 'Suit' };
tjoc == suit; // false
_.isEqual(tjoc, suit); // true
random
/* _.random(min, max); */
_.random(13, 24); // 16
Lodash!
- Drop-in replacement
- Faster
- Better tested
- AMD + CJS compatible
- Use it!
- Require:
paths: { 'underscore': 'path/to/lodash' }
Read the docs!
Tons of awesome stuff.
Underscore for President
By Espen Hovlandsdal
Underscore for President
- 1,511