Factory Functions And The Dry Principle

Creating objects one-by-one is tedious. We can use the first tool in our toolbox, Factory Functions, to automate the work, but this introduces a new problem: memory bloat.

April 25, 20263 min read2 / 7

The first tool in our Object-Oriented Programming toolbox is the Factory Function. In the previous part, we saw how an Object Literal can bundle data and functionality. But in a real app with thousands of users, manually typing every object is untenable. It breaks the DRY (Don't Repeat Yourself) principle.

To solve this, we wrap the creation logic in a function.

The Essentials

  1. Factory Function: A function that returns a new object every time it is called.
  2. Automation: Passing arguments to a function to dynamically populate object properties.
  3. The Memory Problem: Every object created this way gets its own copy of every method, leading to significant memory waste.

Memory Bloat: Redundant Function Copies ExpandMemory Bloat: Redundant Function Copies


Solution 1: Generate Objects Using a Function

Instead of manual creation, we define a userCreator function. This is often called a Factory Function.

JavaScript
function userCreator(name, score) { const newUser = {}; newUser.name = name; newUser.score = score; newUser.increment = function() { newUser.score++; }; return newUser; } const user1 = userCreator("Denver", 3); const user2 = userCreator("Neal", 5); user1.increment();

The Trace

  1. Declare userCreator: Stored in global memory.
  2. Call userCreator("Denver", 3):
    • Create a new Execution Context.
    • Parameters name and score are assigned "Denver" and 3.
    • A newUser object is created.
    • Properties name and score are added.
    • An increment function is defined and stored as a method on newUser.
    • Return the newUser object.
  3. Store in user1: Global memory now has user1 pointing to that object.
  4. Repeat for user2: A brand new object is created with its own brand new increment function.
JavaScript Execution Engine
Thread of Execution
1function userCreator(name, score) {
2 const newUser = {};
3 newUser.name = name; ...
4 newUser.increment = function() { ... };
5 return newUser;
6}
7const user1 = userCreator('Denver', 3);
8const user2 = userCreator('Neal', 5);

Step 1:user1 is created with its own copy of the increment function.

Memory
user1{ name: 'Denver', score: 3, increment: f }
Call Stack
Global
Bottom of Stack

The "Untenable" Problem: Memory Efficiency

This design is easy to reason about. We know exactly where the increment function is: it is right on the object.

However, this is completely inefficient. If we have 1,000 users, we have 1,000 copies of the exact same increment function. If each user has 20 methods (login, logout, delete, update), that's 20,000 functions taking up space in your computer's memory.

The data (name, score) should be unique for every user. But the functionality is identical.

Is there a better way?

Our dream is to have only one copy of the functions, but still allow every user object to access them as if they were their own. To do this, we need to move beyond simple objects and into the Prototype Chain.

Further Reading and Watching