How Lodash functionsIn Searches Prototype Chain
This article explains how the _.functionsIn method in
the Lodash JavaScript library operates, specifically highlighting how it
traverses an object's prototype chain. While standard introspection
methods like _.functions restrict their search to an
object's own direct properties, _.functionsIn iterates
through both own and inherited enumerable properties to locate
functions. Understanding this distinction is essential for properly
inspecting object hierarchies, inherited behavior, and class-like
structures in JavaScript.
Understanding _.functions vs. _.functionsIn
Lodash provides two primary methods for retrieving function names
from an object: _.functions (also aliased as
_.methods) and _.functionsIn.
_.functions(object): Iterates over only the object's own enumerable properties. It mirrors the behavior ofObject.keys()filtered by function types._.functionsIn(object): Iterates over an object's own and inherited enumerable properties. It mirrors the behavior of afor...inloop filtered by function types.
How Prototype Chain Traversal Works in _.functionsIn
When _.functionsIn is called on a target object, it does
not stop at the object's immediate properties. Instead, it walks up the
prototype chain ([[Prototype]] or __proto__)
until it reaches Object.prototype or null.
During this traversal, Lodash collects the property keys, checks whether the associated value is a function, and ensures each function name is added to the resulting array. If a method on a child object has the same name as a method on a parent object, the property is shadowed, meaning the name appears only once in the returned array.
Practical Example
Consider an example involving a constructor function and prototype inheritance:
const _ = require('lodash');
function Animal() {
this.breathe = function() { return 'breathing'; };
}
Animal.prototype.eat = function() { return 'eating'; };
function Dog() {
Animal.call(this);
this.bark = function() { return 'barking'; };
}
Dog.prototype = Object.create(Animal.prototype);
Dog.prototype.fetch = function() { return 'fetching'; };
const myDog = new Dog();
// Inspecting with _.functions
console.log(_.functions(myDog));
// Output: ['bark', 'breathe']
// Inspecting with _.functionsIn
console.log(_.functionsIn(myDog));
// Output: ['bark', 'breathe', 'eat', 'fetch']In the example above, _.functions(myDog) only detects
bark and breathe because they are assigned
directly to the instance via this. In contrast,
_.functionsIn(myDog) traverses the prototype chain to
detect fetch on Dog.prototype and
eat on Animal.prototype.
The Enumerable Constraint
A critical detail in how _.functionsIn navigates the
prototype chain is property enumerability. The method only checks
enumerable properties.
In modern JavaScript (ES6+), methods declared within classes are non-enumerable by default:
class Vehicle {
drive() {}
}
const car = new Vehicle();
console.log(_.functionsIn(car));
// Output: []Because drive is non-enumerable on
Vehicle.prototype, _.functionsIn will not
detect it. To include prototype methods created using ES6 class syntax,
properties must either be defined as enumerable explicitly via
Object.defineProperty or assigned as instance arrow
functions.
Summary
The difference between _.functions and
_.functionsIn is inheritance. Use _.functions
when you only care about methods assigned directly to a specific
instance, and use _.functionsIn when you need a complete
list of callable, enumerable methods available to an object, including
those inherited from its prototype chain.