How Lodash Checks Primitive and Boxed Strings
In JavaScript, strings can exist either as primitive values or as
boxed String wrapper objects created via the
new String() constructor. While standard JavaScript treats
them differently under strict equality and type checks, the Lodash
library provides unified type verification through its
_.isString utility. To handle both representations
accurately without mistaking general objects for strings, Lodash splits
its verification process into a fast typeof evaluation for
primitives and an internal object-tag evaluation for boxed objects.
The Problem with Primitive vs. Boxed Strings
In native JavaScript, primitive strings and boxed string objects exhibit distinct behaviors when examined using native operators:
const primitiveStr = 'hello';
const objectStr = new String('hello');
typeof primitiveStr; // 'string'
typeof objectStr; // 'object'
primitiveStr === objectStr; // falseBecause typeof objectStr yields 'object', a
standard typeof check fails to recognize a boxed string as
string data. Conversely, checking only whether an entity has string
methods or properties can lead to false positives on custom objects.
How Lodash Implements
isString
Lodash bridges this divide in its internal isString
implementation. The source code evaluates the argument using a
conditional branching strategy:
function isString(value) {
const type = typeof value;
return type === 'string' || (
type === 'object' &&
value != null &&
!Array.isArray(value) &&
getTag(value) === '[object String]'
);
}The differentiation occurs across two distinct steps:
Primitive Detection via
typeof:
Lodash first evaluatestypeof value === 'string'. If the value is a string literal or a primitive produced byString(...)withoutnew, the condition evaluates totrueimmediately. This avoids unnecessary function calls for the most common use cases.Boxed Detection via Internal Tags:
If thetypeofcheck returns'object', Lodash proceeds to verify whether the object is a boxed primitive. It confirms the value is non-null and not an array, then calls its internalgetTag()helper.
The Role of
getTag and Object.prototype.toString
Lodash's getTag relies on
Object.prototype.toString.call(value). When JavaScript
executes Object.prototype.toString on a boxed string
object, it reads the internal [[Class]] slot (or
Symbol.toStringTag in modern ECMAScript environments),
returning the string:
Object.prototype.toString.call(new String('hello')); // '[object String]'Standard objects, functions, and other boxed types return different
signatures, such as '[object Object]' or
'[object Number]'. By checking for
[object String], Lodash safely verifies that the object is
genuinely an instance of the String wrapper, even if it
originated from a different execution context or iframe where
instanceof String checks might fail.
Separating Boxed Strings from Primitives
Although _.isString returns true for both
types, you can separate them by combining Lodash functions or pairing
Lodash with native checks:
- Identify Primitives Only: Use native
typeof value === 'string'. - Identify Boxed Strings Only: Check that the value
is recognized as a string by Lodash while also qualifying as an object
via
_.isObjectLike(value) && _.isString(value).
Boxed strings will also return false when checked with
_.isPlainObject(value), ensuring that Lodash does not
misidentify string objects as generic key-value dictionaries.