diff --git a/style/javascript.md b/style/javascript.md index 4d14030..2e021fc 100644 --- a/style/javascript.md +++ b/style/javascript.md @@ -21,7 +21,7 @@ * [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) @@ -29,6 +29,7 @@ * [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) => { ... }` [1](#u1) | `_.bind(function(a, b) { ... }, this)` +bindAll | `obj.method = obj.method.bind(someObj);` [2](#u2) | `_.bindAll(someObj, "method")` +clone | No alternative at the moment! [3](#u3) | +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)` +has (object) | `obj.hasOwnProperty(value)` [4](#u4) | `_.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)` +last | `someArray[someArray.length - 1]` [5](#u5) | `_.last(someArray)` +map | `array.map(mapFn)` | `_.map(array, mapFn)` +max | `Math.max(...array)` | `_.max(array)` +object |
Object.entries(obj).reduce(
(result, [key, val]) => {
    result[key] = value;
    return result;
})
|
\_.object(\_.map(obj, (val, key) => {
    return [key, value];
})
+omit (array) | `array.filter(prop => !props.includes(prop))` | `_.omit(array, props)` +omit (object) |
Object.keys(obj).reduce((result, prop) => {
    if (!props.includes(prop)) {
        result[prop] = attrs[prop];
    }
}, {})
| `_.omit(obj, props)` +once | `$(...).one("click", ...)` | `$(...).on("click", _.once(...))` +once |
{
    method: () => {
        if (this._initDone) { return; }
        this._initDone = true;
        ...
    }
}
| `{ method: _.once(() => { ... }) }` +once |
var getResult = () => {
    let val = $.when(...).then(...);
    getResult = () => val;
    return val;
};
|
var getResult = _.once(() => {
    return $.when(...).then(...);
});
+sortBy | `result = result.sort((a, b) => a.prop - b.prop)` | `_.sortBy(result, "prop")` +sortedIndex | Our custom lodash build. | +throttle | Our custom lodash build. | +values | `Object.values(obj)` | `_.values(obj)` + +1. To be used when you're creating a function and immediately binding its context to `this`. +2. Or use a loop if binding multiple methods. +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! +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)`. +5. If you don't care about destructively modifying the array, you can also use `someArray.pop()``.