How Lodash _.result Resolves Values and Functions

The Lodash _.result method provides a flexible way to retrieve values from deeply nested JavaScript objects by seamlessly handling both static properties and executable functions. This article explores how _.result navigates nested object paths, automatically executes functions with the correct contextual this binding, handles missing keys through fallback values, and compares with standard property access methods like _.get.

Syntax and Core Mechanics

The syntax for _.result is defined as follows:

_.result(object, path, [defaultValue])

Like _.get, the method safely traverses intermediate objects without throwing errors if a reference in the chain is null or undefined.

Dynamic Invocation of Functions

The primary characteristic that distinguishes _.result from _.get is how it handles functions. When resolving a path, _.result checks the type of the resolved target:

  1. Static Values: If the target at the resolved path is a primitive (number, string, boolean) or a plain data structure (object, array), it is returned directly.
  2. Callable Functions: If the target is a function, _.result invokes it immediately and returns its output.

When calling a resolved function, _.result binds the this context to the parent object that contains the function. This ensures methods reliant on instance state or surrounding sibling properties execute with the expected scope.

const user = {
  firstName: 'Jane',
  lastName: 'Doe',
  getFullName: function() {
    return `${this.firstName} ${this.lastName}`;
  },
  role: 'Administrator'
};

// Resolving a function executes it with `this` bound to `user`
_.result(user, 'getFullName'); 
// => "Jane Doe"

// Resolving a standard property returns the raw value
_.result(user, 'role'); 
// => "Administrator"

Deep Nested Traversal

When navigating deep paths, _.result resolves intermediate keys as standard properties until it reaches the final segment in the path. If an intermediate segment is a function, it is not executed; only the leaf node of the designated path is evaluated for execution.

const company = {
  departments: {
    engineering: {
      getHeadcount: function() {
        return 42;
      }
    }
  }
};

_.result(company, 'departments.engineering.getHeadcount');
// => 42

In this case, the this context inside getHeadcount is explicitly bound to engineering.

Handling Default Values

If the path does not exist, or if the resolved target evaluates to undefined, _.result falls back to defaultValue.

The defaultValue parameter also supports dynamic resolution. If defaultValue is a function, _.result invokes it and returns its return value:

const settings = {};

// Fallback to static value
_.result(settings, 'theme', 'dark');
// => "dark"

// Fallback to a function result
_.result(settings, 'timestamp', () => Date.now());
// => current timestamp integer

If the property exists on the object but evaluates explicitly to null, false, or NaN, _.result considers the property defined and will return that value rather than evaluating the default.

_.result vs. _.get

While _.get retrieves properties verbatim, _.result is designed for polymorphic interfaces where an object property might either be a static value or a getter function depending on the application state or model architecture (such as in Backbone.js models).

By abstracting away the check for typeof target === 'function', _.result reduces boilerplate code and prevents runtime errors associated with manually checking and invoking properties across deep nested paths.