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.

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.