Description
Creating Valid Substitutions for Lodash Functions
This document provides a comprehensive guide on creating valid substitutions for Lodash functions, using the _.isBoolean
function as an exemplar.
Retrieving the Original Lodash Functionality
Lodash can be complex, with each function residing in its own file. To simplify the process of determining the original functionality, you can utilize per-method packages provided by Lodash. Follow these steps:
- Visit the npm page of the function you want to substitute. In our example, we visit the npm page of the lodash.isboolean package.
- Click on the
code
tab to access the source code of the package, which is bundled in a single file namedindex.js
.
Here is the content of the index.js
for reference:
/**
* lodash 3.0.3 (Custom Build) <https://lodash.com/>
* Build: `lodash modularize exports="npm" -o ./`
* Copyright 2012-2016 The Dojo Foundation <http://dojofoundation.org/>
* Based on Underscore.js 1.8.3 <http://underscorejs.org/LICENSE>
* Copyright 2009-2016 Jeremy Ashkenas, DocumentCloud and Investigative Reporters & Editors
* Available under MIT license <https://lodash.com/license>
*/
/** `Object#toString` result references. */
var boolTag = '[object Boolean]';
/** Used for built-in method references. */
var objectProto = Object.prototype;
/**
* Used to resolve the [`toStringTag`](http://ecma-international.org/ecma-262/6.0/#sec-object.prototype.tostring)
* of values.
*/
var objectToString = objectProto.toString;
/**
* Checks if `value` is classified as a boolean primitive or object.
*
* @static
* @memberOf _
* @category Lang
* @param {*} value The value to check.
* @returns {boolean} Returns `true` if `value` is correctly classified, else `false`.
* @example
*
* _.isBoolean(false);
* // => true
*
* _.isBoolean(null);
* // => false
*/
function isBoolean(value) {
return value === true || value === false ||
(isObjectLike(value) && objectToString.call(value) == boolTag);
}
/**
* Checks if `value` is object-like. A value is object-like if it's not `null`
* and has a `typeof` result of "object".
*
* @static
* @memberOf _
* @category Lang
* @param {*} value The value to check.
* @returns {boolean} Returns `true` if `value` is object-like, else `false`.
* @example
*
* _.isObjectLike({});
* // => true
*
* _.isObjectLike([1, 2, 3]);
* // => true
*
* _.isObjectLike(_.noop);
* // => false
*
* _.isObjectLike(null);
* // => false
*/
function isObjectLike(value) {
return !!value && typeof value == 'object';
}
module.exports = isBoolean;
Creating the Substitution
Template
Begin with the following template for creating the substitution:
'use strict';
const assert = require('assert');
const _ = require('lodash');
function isBoolean() {
// TODO
}
// Assertions
// TODO
Assertions or tests are essential to ensure the validity of your substitution. You can use the examples provided in the comments of the original function. It is also recommended to add additional tests while implementing the substitution to ensure the function behaves as expected.
In this example, we use Lodash as the reference implementation and do not test it against the expected result.
From the comments in the original function, extract the following assertions:
assert.strictEqual(_.isBoolean(false), isBoolean(false));
assert.strictEqual(_.isBoolean(null), _.isBoolean(null));
Additionally, include the not documented true
case:
assert.strictEqual(_.isBoolean(true), isBoolean(true));
Now, let's start with the implementation.
Implementation
A clear understanding of the original function is crucial:
// isBoolean from lodash
function isBoolean(value) {
return value === true || value === false ||
(isObjectLike(value) && objectToString.call(value) == boolTag);
}
Lodash's isBoolean
explicitly compares against true
and false
. While the typeof
operator might seem sufficient, benchmarking shows that explicitly comparing with true
and false
is faster.
Lodash's isBoolean
uses isObjectLike
in combination with objectToString
. This check is for "instantiated" Boolean
objects.
Keep in mind that using objects of primitive types is not recommended. However, for the sake of creating a valid substitution, it's necessary to implement this check. You can add a comment to the substitution, indicating that the check can be removed if the user is sure that no Boolean
objects are used.
isObjectLike
essentially performs a null check and a typeof check. objectToString
is a reference to Object.prototype.toString
. The variable boolTag
is simply a string reference to the toString
result of a Boolean
object. You can remove this variable and use the string directly.
The final implementation is as follows:
// isBoolean substitute
function isBoolean(value) {
return (
value === true ||
value === false ||
// not necessary if no Boolean objects are used
(
value && // null check
typeof value === 'object' && // check if it is an object
Object.prototype.toString.call(value) === '[object Boolean]' // check if it is a Boolean object
)
)
}
For the Boolean object branch, include assertions/tests:
// Truthy cases
assert.strictEqual(_.isBoolean(Boolean()), isBoolean(Boolean()));
assert.strictEqual(_.isBoolean(Boolean(true)), isBoolean(Boolean(true)));
assert.strictEqual(_.isBoolean(Boolean(false)), isBoolean(Boolean(false)));
// Instantiated Boolean objects
assert.strictEqual(_.isBoolean(new Boolean(true)), isBoolean(new Boolean(true)));
assert.strictEqual(_.isBoolean(new Boolean(false)), isBoolean(new Boolean(false)));
// Falsy cases
assert.strictEqual(_.isBoolean(new String(true)), isBoolean(new String(true)));
assert.strictEqual(_.isBoolean(new String(false)), isBoolean(new String(false)));
Final Result
Here's the final implementation with assertions:
'use strict';
const assert = require('assert');
const _ = require('lodash');
function isBoolean(value) {
return (
value === true ||
value === false ||
// Not necessary if no Boolean objects are used
(
value && // Null check
typeof value === 'object' && // Check if it is an object
Object.prototype.toString.call(value) === '[object Boolean]' // Check if it is a Boolean object
)
)
}
// Assertions
assert.strictEqual(_.isBoolean(false), isBoolean(false));
assert.strictEqual(_.isBoolean(null), _.isBoolean(null));
assert.strictEqual(_.isBoolean(true), isBoolean(true));
// Truthy cases
assert.strictEqual(_.isBoolean(Boolean()), isBoolean(Boolean()));
assert.strictEqual(_.isBoolean(Boolean(true)), isBoolean(Boolean(true)));
assert.strictEqual(_.isBoolean(Boolean(false)), is
Boolean(Boolean(false)));
// Instantiated Boolean objects
assert.strictEqual(_.isBoolean(new Boolean(true)), isBoolean(new Boolean(true)));
assert.strictEqual(_.isBoolean(new Boolean(false)), isBoolean(new Boolean(false)));
// Falsy cases
assert.strictEqual(_.isBoolean(new String(true)), isBoolean(new String(true)));
assert.strictEqual(_.isBoolean(new String(false)), isBoolean(new String(false));
Running this code in Node will demonstrate that all assertions pass.
Publishing the Substitution
You can now propose your substitution to the You-Dont-Need-Lodash-Underscore project. You can open an issue to discuss the substitution or create a pull request directly. Follow the patterns outlined in the Readme.md and add your substitution to the appropriate section, listed alphabetically. Use the Lodash website to identify the correct section.
Transform the assertions into Mocha tests in the test/unit/all.js
file.
To determine browser support, refer to resources such as the caniuse website or the Mozilla Developer Network.