Why Lodash _.template Uses With and Strict Mode
This article explores why the Lodash _.template utility
historically relies on JavaScript’s with statement to scope
data variables within compiled templates. It examines the ergonomics
behind this design choice, the technical incompatibilities and
performance penalties it introduces in ECMAScript strict mode, and how
configuring the variable option resolves these issues.
The Purpose of
with in _.template
Lodash’s _.template compiles micro-template strings into
executable JavaScript functions. By default, it wraps the internal
template code inside a with (obj) statement.
The primary reason for this approach is syntax ergonomics. In a template, developers typically want to reference properties directly:
<div>Hello, <%= name %>!</div>Without a with statement or an explicit namespace, the
template engine would need to do one of two things to resolve
name:
- Parse the template expressions via an AST (Abstract Syntax Tree) to
discover every variable identifier and explicitly declare or destructure
them (e.g.,
const { name } = obj;). - Require users to prefix every variable with a data namespace, such
as
<%= data.name %>.
The with statement offers a lightweight, low-overhead
mechanism for the engine. It adds the supplied data object to the top of
the scope chain, allowing bare variable identifiers like
name to resolve dynamically to obj.name at
runtime without requiring static analysis during template
compilation.
The Strict Mode Implications
While convenient for template authoring, the with
statement introduces severe conflicts with modern JavaScript
environments that enforce strict mode ("use strict";).
1. Syntax Errors in Strict Mode
ECMAScript 5 banned with statements outright in strict
mode. If a project executes under strict mode—which is standard in ES
modules, modern bundlers, and TypeScript environments—any function
containing a with statement throws a runtime
SyntaxError:
SyntaxError: Strict mode code may not include a with statement
Because Lodash creates the compiled template function using the
Function constructor, the generated code runs in sloppy
mode by default unless strict mode is explicitly injected. However, if
the compilation environment or downstream tooling attempts to enforce
strict mode on all evaluated code, the template will immediately
fail.
2. Engine Optimization and Performance Costs
Even when strict mode is not explicitly enforced, the
with block negatively impacts performance. JavaScript
engines like V8 optimize property lookups using static lexical scoping
and hidden classes.
Because with dynamically injects an arbitrary object
into the scope chain, the engine cannot determine at compile time
whether an identifier refers to an object property or an outer scope
variable. As a result:
- The JavaScript engine must fall back to slow, dynamic dictionary lookups at runtime.
- Inlining and JIT (Just-In-Time) optimizations for the template function are largely disabled.
The Solution: Using the
variable Setting
Lodash provides a built-in mechanism to eliminate the
with statement entirely by defining the
variable property in _.templateSettings or
within the individual template options.
// Specifying an explicit namespace
const compiled = _.template('<div>Hello, <%= data.name %>!</div>', {
variable: 'data'
});
compiled({ name: 'World' });When the variable option is defined:
- Lodash avoids wrapping the function body in a
withblock. - Values are accessed directly off the designated identifier (e.g.,
data.name), allowing the JavaScript engine to statically optimize the scope chain. - The resulting function is fully compliant with strict mode semantics.
Defining an explicit data variable produces faster, modern, and strict-mode-safe template functions at the cost of requiring callers to prefix template variables with the chosen namespace.