# 7 Ways to Create Objects in JavaScript: Complete Guide to Object Creation Patterns

> Explore seven JavaScript object creation patterns. Learn when to use literals, classes, factories & more for efficient memory, encapsulation & inheritance.

- Repository: [Leonardo Maldonado/33-js-concepts](https://github.com/leonardomso/33-js-concepts)
- Tags: deep-dive
- Published: 2026-03-04

---

**JavaScript offers seven distinct patterns for creating objects—from simple literals to ES6 classes and factory composition—each offering unique trade-offs in memory efficiency, encapsulation, and inheritance flexibility.**

The `leonardomso/33-js-concepts` repository demonstrates every major object creation technique through executable test suites. Understanding the different ways to create objects in JavaScript allows you to select the optimal pattern for your specific use case, whether you need quick data structures or complex domain models with true privacy.

## Object Literals for Quick Data Structures

The **object literal** syntax is the most concise way to create a plain object with `Object.prototype` as its prototype chain.

Use this approach for configuration objects, one-off data structures, or any scenario where you do not need shared behavior across multiple instances. In [`tests/object-oriented/object-creation-prototypes/object-creation-prototypes.test.js`](https://github.com/leonardomso/33-js-concepts/blob/main/tests/object-oriented/object-creation-prototypes/object-creation-prototypes.test.js), the repository demonstrates this pattern for simple player definitions:

```javascript
const player = { name: 'Alice', health: 100 };
console.log(player.name); // Alice

```

Object literals excel when you need immediate, readable object instantiation without the overhead of constructors or classes.

## Object.create() for Explicit Prototype Chains

When you need an object that inherits from a specific prototype without invoking a constructor, use **`Object.create(proto, descriptors?)`**.

This method creates a new object with the explicit prototype you supply, making it ideal for setting up prototype chains directly. The test file [`object-creation-prototypes.test.js`](https://github.com/leonardomso/33-js-concepts/blob/main/object-creation-prototypes.test.js) shows this pattern when establishing inheritance relationships:

```javascript
const proto = { attack() { return `${this.name} attacks!`; } };
const player = Object.create(proto);
player.name = 'Bob';
player.health = 100;
console.log(player.attack()); // Bob attacks!

```

Prefer `Object.create()` when you want fine-grained control over the prototype chain or when working with objects that should not follow the constructor instantiation pattern.

## Factory Functions for Encapsulation and Privacy

**Factory functions** are regular functions that return new objects, closing over private variables to create true encapsulation.

This pattern avoids the `new` keyword entirely and allows you to hide implementation details via closure. According to [`tests/object-oriented/factories-classes/factories-classes.test.js`](https://github.com/leonardomso/33-js-concepts/blob/main/tests/object-oriented/factories-classes/factories-classes.test.js), factories are the preferred pattern when you need private state without the complexity of class syntax:

```javascript
function createPlayer(name) {
  let health = 100;               // private
  return {
    name,
    attack() { return `${name} attacks!`; },
    getHealth() { return health; }
  };
}
const p = createPlayer('Carol');
console.log(p.attack()); // Carol attacks!

```

Factory functions shine in scenarios requiring data privacy or when you want to avoid the `this` binding complexities associated with constructors.

## Constructor Functions with the `new` Keyword

**Constructor functions** capitalize on JavaScript’s `new` operator to create objects with a shared prototype.

When invoked with `new`, JavaScript creates an empty object, sets its `[[Prototype]]` to the function’s `.prototype` property, executes the constructor body with `this` bound to the new object, and returns the object. The [`factories-classes.test.js`](https://github.com/leonardomso/33-js-concepts/blob/main/factories-classes.test.js) file demonstrates this legacy pattern:

```javascript
function Player(name) {
  this.name = name;
  this.health = 100;
}
Player.prototype.attack = function () {
  return `${this.name} attacks!`;
};
const p = new Player('Dave');
console.log(p.attack()); // Dave attacks!

```

Use constructor functions when working with legacy codebases or when you need prototype sharing without modern class syntax, though ES6 classes are generally preferred for new development.

## ES6 Classes for Modern Object-Oriented Programming

**ES6 classes** provide syntactic sugar over constructor functions, offering a declarative syntax for constructors, prototype methods, static members, and inheritance.

Classes support `extends`, `super`, static fields, and private fields (`#`), making them ideal for modern codebases that favor clear object-oriented structures. As implemented in [`tests/object-oriented/factories-classes/factories-classes.test.js`](https://github.com/leonardomso/33-js-concepts/blob/main/tests/object-oriented/factories-classes/factories-classes.test.js), classes provide the most self-documenting structure for complex domain models:

```javascript
class Player {
  #health = 100;                 // truly private
  constructor(name) {
    this.name = name;
  }
  attack() {
    return `${this.name} attacks!`;
  }
  getHealth() {
    return this.#health;
  }
}
const p = new Player('Eve');
console.log(p.attack()); // Eve attacks!

```

ES6 classes are the standard choice for modern applications requiring hierarchical inheritance and strong typing conventions.

## Class Fields and Arrow Functions for Auto-Binding

**Class fields** and **arrow-function methods** allow you to declare instance properties directly in the class body and ensure `this` binding for callbacks.

This pattern eliminates the need for manual binding in constructors, as arrow functions lexically capture `this` from the class instance. The repository demonstrates this in [`factories-classes.test.js`](https://github.com/leonardomso/33-js-concepts/blob/main/factories-classes.test.js) under the section *Arrow Function Class Fields*:

```javascript
class Button {
  count = 0;
  handleClick = () => {
    this.count++;
    return this.count;
  };
}
const btn = new Button();
const click = btn.handleClick;
console.log(click()); // 1 – `this` stays bound

```

Use this approach when creating event handlers or callback methods where maintaining the correct `this` context is critical, or when you need per-instance values without explicitly defining a constructor.

## Composition via Factories for Flexible Behavior

**Composition (mixins)** using factories allows you to combine multiple behaviors into a single object without deep inheritance hierarchies.

Small factory functions return behavior objects that are spread into a new object, enabling orthogonal, reusable functionality. This pattern is demonstrated in [`factories-classes.test.js`](https://github.com/leonardomso/33-js-concepts/blob/main/factories-classes.test.js) under the *Factory Composition* section:

```javascript
function canWalk(state) {
  return {
    walk() {
      state.position += state.speed;
      return `${state.name} walks to ${state.position}`;
    }
  };
}
function createDuck(name) {
  const state = { name, position: 0, speed: 2 };
  return {
    name,
    ...canWalk(state)
  };
}
const d = createDuck('Donald');
console.log(d.walk()); // Donald walks to 2

```

Prefer composition over inheritance when you need to mix multiple independent behaviors or avoid the rigidity of class hierarchies.

## Architectural Considerations When Creating Objects

Selecting the right object creation pattern requires evaluating five key architectural concerns:

- **Memory efficiency**: Classes and constructor functions share prototype methods across all instances, referencing the same function object. Factory functions that recreate methods per instance consume additional memory.
- **True privacy**: ES2022 private fields (`#field`) are truly hidden from external code. Factory closures also provide privacy, though with less explicit syntax than the hash prefix notation.
- **`instanceof` semantics**: Only class-based and constructor-function objects participate in `instanceof` checks against their constructor. Plain objects from factories return `instanceof Object` only.
- **Extensibility**: Classes support clean hierarchical extension via `extends` and `super`. Factory composition works through object spreading, offering flexibility but no built-in prototype chain for `instanceof` checks.
- **Ease of use**: Object literals offer maximum conciseness for static data, while class syntax provides the most readable structure for large domain models.

## Summary

- **Object literals** provide the fastest syntax for one-off data structures and configuration objects.
- **`Object.create()`** enables explicit prototype assignment without constructor invocation.
- **Factory functions** deliver encapsulation via closures and avoid `new` keyword requirements.
- **Constructor functions** offer legacy-compatible prototype sharing for existing codebases.
- **ES6 classes** supply modern, declarative syntax with support for static members, private fields, and inheritance.
- **Class fields and arrow functions** solve `this` binding issues for callbacks and event handlers.
- **Composition via mixins** creates flexible, orthogonal behaviors without deep inheritance chains.

## Frequently Asked Questions

### What is the most memory-efficient way to create objects in JavaScript?

Classes and constructor functions are the most memory-efficient patterns because they place methods on the shared prototype object. All instances reference the same function object rather than creating new function instances per object, as seen in the [`factories-classes.test.js`](https://github.com/leonardomso/33-js-concepts/blob/main/factories-classes.test.js) test suite where prototype methods are defined once and shared across all `Player` instances.

### How do I create truly private properties in JavaScript objects?

You have two robust options for true privacy. **ES2022 private fields** (syntax: `#health`) are hard-private and inaccessible outside the class body. Alternatively, **factory functions** close over variables in the function scope, making them accessible only to the returned object's methods. Both patterns are demonstrated in [`tests/object-oriented/factories-classes/factories-classes.test.js`](https://github.com/leonardomso/33-js-concepts/blob/main/tests/object-oriented/factories-classes/factories-classes.test.js).

### When should I use factory functions instead of ES6 classes?

Use **factory functions** when you need to avoid the `new` keyword, require true privacy through closures, or want to return different object shapes conditionally. Use **ES6 classes** when you need the `instanceof` operator to work, require inheritance via `extends`, or want the explicit structure of static methods and private fields that the class syntax provides.

### Can I mix different object creation patterns in the same project?

Yes, modern JavaScript projects frequently combine patterns. You might use **object literals** for configuration constants, **ES6 classes** for domain models requiring inheritance, and **factory composition** for shared behaviors across unrelated classes. The `33-js-concepts` repository itself mixes these patterns in [`object-creation-prototypes.test.js`](https://github.com/leonardomso/33-js-concepts/blob/main/object-creation-prototypes.test.js) and [`factories-classes.test.js`](https://github.com/leonardomso/33-js-concepts/blob/main/factories-classes.test.js) to demonstrate their interoperability.