Lodash _.size Behavior with Custom Length Getter
When evaluating an object with a custom .length getter
in Lodash, _.size will return the exact value produced by
that getter, provided the returned value meets Lodash’s definition of a
valid array-like length. If the custom getter returns an invalid length
(such as a negative number, a non-integer, or a non-numeric type),
Lodash bypasses the getter and instead falls back to returning the total
count of the object’s enumerable own string-keyed properties.
The Internal Mechanics of
_.size
To understand why this happens, look at how Lodash determines the size of an incoming collection:
- Null/Undefined Check: If the target is
nullorundefined,0is returned immediately. - Array-Like Validation (
isArrayLike): Lodash checks if the value is array-like by checking two primary conditions:- The value is not
nulland is not afunction. - The value possesses a valid length property, validated through the
internal
isLength(value.length)function.
- The value is not
- Dispatch:
- If
isArrayLikeevaluates totrue, Lodash directly accesses and returnscollection.length(with a special case for strings to handle Unicode symbols properly). - If
isArrayLikeevaluates tofalse, Lodash checks if the object is aMaporSetto return their.size. Otherwise, it falls through to base object handling, returningObject.keys(collection).length.
- If
Definition of a Valid Length
For a custom .length getter to trigger the array-like
branch, the value it returns must satisfy isLength:
- It must be of type
number. - It must be greater than or equal to
0. - It must be an integer (
value % 1 == 0). - It must be less than or equal to
Number.MAX_SAFE_INTEGER(9007199254740991).
Behavior Examples
1. Valid Length Returned by Getter
const obj = {
a: 1,
b: 2,
get length() {
return 42;
}
};
_.size(obj); // Returns: 42Because 42 is a safe, non-negative integer,
isArrayLike(obj) resolves to true. Lodash
treats the object as array-like and returns obj.length,
which is 42, completely ignoring the fact that the object
only has two own enumerable data properties (a and
b).
2. Invalid Length Returned by Getter
const obj = {
a: 1,
b: 2,
get length() {
return -5; // Negative, not a valid length
}
};
_.size(obj); // Returns: 3Because -5 fails the isLength check,
isArrayLike(obj) resolves to false. Lodash
treats obj as a plain object and counts its enumerable own
properties. Because getter properties defined on object literals are
enumerable by default, the keys counted are
['a', 'b', 'length'], resulting in a return value of
3.
Getter Invocation Side Effect
Because of how _.size and isArrayLike are
structured, custom getters are invoked more than once during a single
call:
- First invocation occurs during the
isLength(collection.length)check insideisArrayLike. - Second invocation occurs when
collection.lengthis read to return the final size value.
If the custom getter performs side effects or returns dynamic numbers, it will be executed twice, and the returned size will reflect the value from the second read.