-
Notifications
You must be signed in to change notification settings - Fork 146
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Documentation on moving off of Underscore to ES6/7 #28
Changes from all commits
226d519
4253826
0cd30a9
710a7db
069c1dd
38d58e7
40d88d2
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -21,14 +21,15 @@ | |
* [Avoid href="#" for JavaScript triggers](#avoid-href-for-javascript-triggers) | ||
* [Use modules, not global variables](#use-modules-not-global-variables) | ||
* [ES6/7 rules](#es67-rules) | ||
* [Use => instead of bind(this) ](#use--instead-of-bind) | ||
* [Use => instead of bind(this) ](#use--instead-of-bindthis) | ||
* [Use backticks for string interpolation](#use-backticks-for-string-interpolation) | ||
* [Do not use ES6 classes for React classes](#do-not-use-es6-classes-for-react-classes) | ||
* [Do not use async/await or generators](#do-not-use-asyncawait-or-generators) | ||
* [Do not use Set or Map ](#do-not-use-set-or-map) | ||
* [Use let and const for new files; do not use var ](#use-let-and-const-for-new-files-do-not-use-var) | ||
* [Library rules](#library-rules) | ||
* [Use $ for jQuery](#use--for-jquery) | ||
* [Don't use Underscore](#dont-use-underscore) | ||
|
||
---- | ||
|
||
|
@@ -552,3 +553,52 @@ Yes: | |
```js | ||
$(".some-class span").hide(); | ||
``` | ||
|
||
#### Don't use Underscore | ||
|
||
We use ES6/7 which includes many of the features of Underscore.js! Using Underscore should be avoided in favor of these native language features. | ||
|
||
There are a couple of methods that are sufficiently complicated and don't have a direct equivalent so instead we have a [custom-built](https://lodash.com/custom-builds) copy of [lodash](https://lodash.com/) containing only those specific methods. You can find this file at: `third_party/javascript-khansrc/lodash/lodash.js` along with instructions on how to build it and exactly what methods are included. | ||
|
||
What follows is a method-by-method set of equivalents for what Underscore provides and what you could be using in ES6/7 instead: | ||
|
||
Method | Use... | ...instead of | ||
--------- | ------------------------------------- | ---------------------- | ||
bind | `fn.bind(someObj, args)` | `_.bind(fn, someObj, args)` | ||
bind | `(a, b) => { ... }` <sup>[1](#u1)</sup> | `_.bind(function(a, b) { ... }, this)` | ||
bindAll | `obj.method = obj.method.bind(someObj);` <sup>[2](#u2)</sup> | `_.bindAll(someObj, "method")` | ||
clone | No alternative at the moment! <sup>[3](#u3)</sup> | | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. What about the spread operator, like we use to replace There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
For the Underscore use of There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @matchu I'm mostly concerned about the case of |
||
debounce | Our custom lodash build. | | ||
defer | `setTimeout(fn, 0);` | `_.defer(fn);` | ||
delay | `setTimeout(fn, 2000);` | `_.delay(fn, 2000);` | ||
each (array) | `array.forEach((val, i) => {})` | `_.each(array, (val, i) => {})` | ||
each (array) | `for (const val of array) {}` | `_.each(array, fn)` | ||
each (object) | `for (const [key, val] of Object.entries(obj)) {}` | `_.each(obj, fn)` | ||
extend (new) | `{...options, prop: 1}` | `_.extend({}, options, {prop: 1})` | ||
extend (assign) | `Object.assign(json, this.model.toJSON())` | `_.extend(json, this.model.toJSON())` | ||
filter | `array.filter(checkFn)` | `_.filter(array, checkFn)` | ||
has (array) | `array.includes(value)` | `_.has(array, value)` | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
The |
||
has (object) | `obj.hasOwnProperty(value)` <sup>[4](#u4)</sup> | `_.has(obj, value)` | ||
isArray | `Array.isArray(someObj)` | `_.isArray(someObj)` | ||
isFunction | `typeof fn === "function"` | `_.isFunction(fn)` | ||
isString | `typeof obj === "string"` | `_.isString(obj)` | ||
keys | `Object.keys(obj)` | `_.keys(obj)` | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
last | `someArray[someArray.length - 1]` <sup>[5](#u5)</sup> | `_.last(someArray)` | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
map | `array.map(mapFn)` | `_.map(array, mapFn)` | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
One cool bit is that Lodash and Underscore allow callback shorthands. Like array.map(function(value) {
return value != null && value.id;
}); and array.filter(array, function(value) {
return value != null && value.a != null &&
Array.isArray(value.a.b) && value.a.b.includes(3) &&
value.a.c === true;
}); An added bonus is that Lodash enables shortcut fusion for its chaining syntax, so for example: _(arr10k).map(square).filter(isOdd).slice(0, 4).value();
// => is 8 iterations
arr10k.map(square).filter(isOdd).slice(0, 4);
// => is 20,000 iterations |
||
max | `Math.max(...array)` | `_.max(array)` | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
object | <pre>Object.entries(obj).reduce(<br>(result, [key, val]) => {<br> result[key] = value;<br> return result;<br>})</pre> | <pre>\_.object(\_.map(obj, (val, key) => {<br> return [key, value];<br>})</pre> | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Lodash has |
||
omit (array) | `array.filter(prop => !props.includes(prop))` | `_.omit(array, props)` | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
While If you want to remove elements from an array in Lodash/Underscore there's non-mutating methods like |
||
omit (object) | <pre>Object.keys(obj).reduce((result, prop) => {<br> if (!props.includes(prop)) {<br> result[prop] = attrs[prop];<br> }<br>}, {})</pre> | `_.omit(obj, props)` | ||
once | `$(...).one("click", ...)` | `$(...).on("click", _.once(...))` | ||
once | <pre>{<br> method: () => {<br> if (this._initDone) { return; }<br> this._initDone = true;<br> ...<br> }<br>}</pre>| `{ method: _.once(() => { ... }) }`</pre> | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Wow, the source code for this looks really messy, but the end result is really nice! |
||
once | <pre>var getResult = () => {<br> let val = $.when(...).then(...);<br> getResult = () => val;<br> return val;<br>};</pre> | <pre>var getResult = _.once(() => {<br> return $.when(...).then(...);<br>});</pre> | ||
sortBy | `result = result.sort((a, b) => a.prop - b.prop)` | `_.sortBy(result, "prop")` | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
The |
||
sortedIndex | Our custom lodash build. | | ||
throttle | Our custom lodash build. | | ||
values | `Object.values(obj)` | `_.values(obj)` | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Something to keep in mind is the shims provided by babel can weigh in heavier than Lodash (kitchen-sink) and, depending on the version, may be larger than jQuery itself. For a different take you might also dig my talk on shims and libs. |
||
|
||
1. To be used when you're creating a function and immediately binding its context to `this`. <b id="u1"></b> | ||
2. Or use a loop if binding multiple methods. <b id="u2"></b> | ||
3. No alternative at the moment! If you need it then you should add it to the compiled version of lodash and then update this guide to mention that it now exists! <b id="u3"></b> | ||
4. While we recommend using `obj.hasOwnProperty(prop)` it is possible that the object could have a method named `hasOwnProperty` that does something else, causing this call to break. The likelihood of this happening is extremely slim - but if you're developing something that you wish to work absolutely everywhere you may want to do something like `Object.prototype.hasOwnProperty.call(obj, prop)`. <b id="u4"></b> | ||
5. If you don't care about destructively modifying the array, you can also use `someArray.pop()``. <b id="u5"></b> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just popping in to add context, tweaks, & insights for some of the items in this PR (expect multiple comments).
Disclaimer: Behaviors may differ from Underscore to Lodash so I'll be speaking from the Lodash point of view for the most part. Take from the comments what you will, no pressure. [Update] Continued on #43 😎
Lodash has over 200 totally modular methods and Underscore has ~115 methods, which is far more than the handful of ES6/7 built-ins and constructs. The line above gives me the vibe that ES6/7 can full-on replace many/most of Underscore/Lodash which is not really the case. jQuery and Underscore have both had the you-may-not-need treatment so I figure you all, especially Resig, would be sensitive to that perception.
Lodash embraces, enhances, and works great combo'ed with ES6/7. If possible, I'd like to avoid perpetuating the sentiment that Lodash/Underscore and ES6/7 are somehow mutually exclusive or that Lodash/Underscore are somehow outdated / no-longer-useful. JavaScript will always have gaps that need filling and Lodash will continue to adapt and evolve to fill them.
More code tweaks / comments below...