How to Create Objects in JavaScript: Full Guide

Learn every way to create objects in JavaScript including object literals, constructors, Object.create, classes, factory functions, and dynamic property patterns with practical code examples.

JavaScriptbeginner
14 min read

JavaScript provides multiple ways to create objects, each suited to different situations. Whether you need a simple configuration object, a reusable blueprint for dozens of instances, or a prototype-based inheritance chain, picking the right creation pattern matters. Understanding all available options helps you write cleaner, more maintainable code and choose the pattern that fits your project's needs.

This guide walks you through every object creation method in JavaScript, from the basic object literal syntax to modern ES6 classes. You will see practical examples for each approach and learn when to use which pattern.

Object Literal Syntax

The object literal is the most common and simplest way to create objects in JavaScript. You wrap key-value pairs inside curly braces:

javascriptjavascript
const user = {
  firstName: "Alice",
  lastName: "Johnson",
  age: 28,
  email: "alice@example.com",
  isActive: true
};
 
console.log(user.firstName); // "Alice"
console.log(user.age);       // 28

When to Use Object Literals

Object literals work best for one-off objects where you do not need to create multiple instances with the same structure. Configuration objects, API response mappings, and function option parameters are ideal use cases:

javascriptjavascript
const config = {
  apiUrl: "https://api.example.com",
  timeout: 5000,
  retries: 3,
  debug: false
};
 
const searchParams = {
  query: "javascript objects",
  page: 1,
  limit: 20,
  sortBy: "relevance"
};

Shorthand Property Names (ES6)

When the variable name matches the property name, you can use shorthand syntax:

javascriptjavascript
const name = "Alice";
const age = 28;
const role = "developer";
 
// Without shorthand
const userOld = { name: name, age: age, role: role };
 
// With shorthand (ES6)
const userNew = { name, age, role };
 
console.log(userNew); // { name: "Alice", age: 28, role: "developer" }

Computed Property Names

You can use expressions inside square brackets as property names:

javascriptjavascript
const field = "email";
const prefix = "user";
 
const obj = {
  [field]: "alice@example.com",
  [`${prefix}Name`]: "Alice",
  [`${prefix}Id`]: 42
};
 
console.log(obj.email);    // "alice@example.com"
console.log(obj.userName); // "Alice"
console.log(obj.userId);   // 42

Constructor Functions

Constructor functions let you create multiple objects with the same structure. By convention, constructor names start with an uppercase letter. You call them with the new keyword:

javascriptjavascript
function User(firstName, lastName, age) {
  this.firstName = firstName;
  this.lastName = lastName;
  this.age = age;
  this.isActive = true;
}
 
User.prototype.getFullName = function() {
  return `${this.firstName} ${this.lastName}`;
};
 
User.prototype.greet = function() {
  return `Hi, I'm ${this.getFullName()}, age ${this.age}`;
};
 
const alice = new User("Alice", "Johnson", 28);
const bob = new User("Bob", "Smith", 32);
 
console.log(alice.getFullName()); // "Alice Johnson"
console.log(bob.greet());         // "Hi, I'm Bob Smith, age 32"
console.log(alice instanceof User); // true

What new Does Behind the Scenes

When you call new User(...), JavaScript performs four steps:

StepActionResult
1Creates a new empty object{}
2Links the object's prototype to User.prototypeInherits methods
3Calls User() with this bound to the new objectProperties assigned
4Returns the new object (unless the function returns another object)Instance ready

Forgetting new

Calling a constructor without new causes this to point to the global object (or undefined in strict mode), creating bugs that are hard to track:

javascriptjavascript
// WRONG: missing new
const broken = User("Charlie", "Brown", 25);
console.log(broken); // undefined (no return statement)
// In non-strict mode, firstName is now on the global object!
 
// Safe guard pattern
function SafeUser(name) {
  if (!(this instanceof SafeUser)) {
    return new SafeUser(name);
  }
  this.name = name;
}

ES6 Classes

Classes are syntactic sugar over constructor functions and prototypes. They provide a cleaner, more familiar syntax for creating objects with shared methods:

javascriptjavascript
class Product {
  constructor(name, price, category) {
    this.name = name;
    this.price = price;
    this.category = category;
    this.inStock = true;
  }
 
  getFormattedPrice() {
    return `$${this.price.toFixed(2)}`;
  }
 
  applyDiscount(percent) {
    this.price = this.price * (1 - percent / 100);
    return this;
  }
 
  toString() {
    return `${this.name} (${this.getFormattedPrice()})`;
  }
}
 
const laptop = new Product("MacBook Pro", 2499.99, "Electronics");
console.log(laptop.getFormattedPrice()); // "$2499.99"
 
laptop.applyDiscount(10);
console.log(laptop.toString()); // "MacBook Pro ($2249.99)"

Class vs Constructor Function Comparison

FeatureConstructor FunctionES6 Class
Syntaxfunction Name() {}class Name {}
MethodsAdded to .prototype manuallyDefined inside class body
HoistingFunction is hoistedClass is NOT hoisted
new requiredNo (fails silently)Yes (throws error without new)
typeof result"function""function"
InheritanceObject.create + manual wiringextends keyword
Static methodsName.method = ...static method() {}

Static Methods and Properties

Static members belong to the class itself, not to instances:

javascriptjavascript
class MathHelper {
  static PI = 3.14159265359;
 
  static circleArea(radius) {
    return MathHelper.PI * radius * radius;
  }
 
  static celsiusToFahrenheit(celsius) {
    return (celsius * 9) / 5 + 32;
  }
}
 
console.log(MathHelper.circleArea(5));          // 78.5398...
console.log(MathHelper.celsiusToFahrenheit(0)); // 32
// MathHelper.PI is accessible without creating an instance

Object.create()

Object.create() creates a new object with a specified prototype. This gives you direct control over the prototype chain without using constructors or classes:

javascriptjavascript
const animal = {
  type: "Unknown",
  speak() {
    return `The ${this.type} makes a sound`;
  },
  describe() {
    return `I am a ${this.type}`;
  }
};
 
const dog = Object.create(animal);
dog.type = "Dog";
dog.bark = function() {
  return "Woof!";
};
 
console.log(dog.speak());    // "The Dog makes a sound"
console.log(dog.bark());     // "Woof!"
console.log(dog.describe()); // "I am a Dog"
 
// Prototype chain verification
console.log(Object.getPrototypeOf(dog) === animal); // true

Object.create with Property Descriptors

You can pass property descriptors as the second argument for fine-grained control:

javascriptjavascript
const base = { role: "user" };
 
const admin = Object.create(base, {
  name: {
    value: "Admin",
    writable: true,
    enumerable: true,
    configurable: true
  },
  level: {
    value: 10,
    writable: false,
    enumerable: true,
    configurable: false
  }
});
 
console.log(admin.name);  // "Admin"
console.log(admin.level); // 10
console.log(admin.role);  // "user" (inherited from prototype)
 
admin.level = 20; // Silently fails (writable: false)
console.log(admin.level); // Still 10

Factory Functions

Factory functions are regular functions that return new objects. They do not require new and offer more flexibility than constructors:

javascriptjavascript
function createUser(firstName, lastName, role) {
  const fullName = `${firstName} ${lastName}`;
 
  return {
    firstName,
    lastName,
    role,
    getFullName() {
      return fullName;
    },
    hasPermission(action) {
      const permissions = {
        admin: ["read", "write", "delete"],
        editor: ["read", "write"],
        viewer: ["read"]
      };
      return (permissions[role] || []).includes(action);
    }
  };
}
 
const admin = createUser("Alice", "Johnson", "admin");
const viewer = createUser("Bob", "Smith", "viewer");
 
console.log(admin.getFullName());         // "Alice Johnson"
console.log(admin.hasPermission("delete")); // true
console.log(viewer.hasPermission("write")); // false

Factory Functions vs Constructors

AspectFactory FunctionConstructor/Class
Calling syntaxcreateUser(...)new User(...)
this bindingNo this issuesthis can be lost
instanceof checkNot possibleuser instanceof User works
Private dataClosures (true privacy)Conventions only (_name)
MemoryEach instance gets own methodsMethods shared via prototype
FlexibilityCan return any object typeAlways returns same type

Encapsulation with Closures

Factory functions achieve true private variables through closures because the returned object can only access private data through exposed methods:

javascriptjavascript
function createBankAccount(owner, initialBalance) {
  let balance = initialBalance; // Truly private
  const transactions = [];      // Truly private
 
  return {
    owner,
    getBalance() {
      return balance;
    },
    deposit(amount) {
      if (amount <= 0) throw new Error("Deposit must be positive");
      balance += amount;
      transactions.push({ type: "deposit", amount, date: new Date() });
      return balance;
    },
    withdraw(amount) {
      if (amount <= 0) throw new Error("Withdrawal must be positive");
      if (amount > balance) throw new Error("Insufficient funds");
      balance -= amount;
      transactions.push({ type: "withdrawal", amount, date: new Date() });
      return balance;
    },
    getTransactionHistory() {
      return [...transactions]; // Return copy, not reference
    }
  };
}
 
const account = createBankAccount("Alice", 1000);
account.deposit(500);
account.withdraw(200);
console.log(account.getBalance()); // 1300
console.log(account.balance);      // undefined (truly private!)

new Object() Constructor

The built-in Object constructor creates empty objects. This is equivalent to the literal {} but less common:

javascriptjavascript
const obj1 = new Object();
obj1.name = "Alice";
obj1.age = 28;
 
// Equivalent to:
const obj2 = {};
obj2.name = "Alice";
obj2.age = 28;
 
// Object literal is preferred (shorter and clearer):
const obj3 = { name: "Alice", age: 28 };

You will rarely see new Object() in modern JavaScript. The literal syntax is shorter, faster to parse, and universally preferred.

Object.assign() for Merging

Object.assign() copies properties from one or more source objects into a target object. This is useful for creating objects by combining smaller pieces:

javascriptjavascript
const defaults = {
  theme: "light",
  language: "en",
  notifications: true,
  fontSize: 16
};
 
const userPreferences = {
  theme: "dark",
  fontSize: 18
};
 
const settings = Object.assign({}, defaults, userPreferences);
console.log(settings);
// { theme: "dark", language: "en", notifications: true, fontSize: 18 }

The spread operator provides a more readable alternative for the same operation:

javascriptjavascript
const settings = { ...defaults, ...userPreferences };

Best Practices for Object Creation

  1. Use object literals for simple, one-off objects (config, options, data maps)
  2. Use classes when you need multiple instances with shared methods and inheritance
  3. Use factory functions when you need true private data or conditional object shapes
  4. Use Object.create when you need explicit prototype chain control
  5. Prefer shorthand properties ({ name } over { name: name }) for cleaner code
  6. Freeze immutable objects with Object.freeze() to prevent accidental modifications
javascriptjavascript
const API_CONFIG = Object.freeze({
  baseUrl: "https://api.example.com",
  version: "v2",
  timeout: 5000
});
 
API_CONFIG.timeout = 10000; // Silently fails (frozen)
console.log(API_CONFIG.timeout); // Still 5000

Common Mistakes to Avoid

Modifying Shared Prototype Objects

javascriptjavascript
// WRONG: mutating a shared prototype
const proto = { items: [] };
const a = Object.create(proto);
const b = Object.create(proto);
 
a.items.push("task 1");
console.log(b.items); // ["task 1"] - both share the SAME array!
 
// CORRECT: initialize own properties
const c = Object.create(proto);
c.items = []; // Own property shadows prototype
c.items.push("task 1");
console.log(proto.items); // [] - prototype unchanged

Using Object Literals Inside Loops

javascriptjavascript
// Inefficient: creates new method copies each iteration
const users = [];
for (let i = 0; i < 1000; i++) {
  users.push({
    id: i,
    getName() { return `User ${this.id}`; } // 1000 copies of this function
  });
}
 
// Better: use a class or constructor (shared methods via prototype)
class User {
  constructor(id) { this.id = id; }
  getName() { return `User ${this.id}`; } // One copy shared by all instances
}
 
const betterUsers = [];
for (let i = 0; i < 1000; i++) {
  betterUsers.push(new User(i));
}

Real-World Example: Form Builder

Here is a practical example combining multiple creation patterns to build a form configuration system:

javascriptjavascript
class FormField {
  constructor(name, type, options = {}) {
    this.name = name;
    this.type = type;
    this.required = options.required || false;
    this.defaultValue = options.defaultValue || "";
    this.validators = options.validators || [];
  }
 
  validate(value) {
    if (this.required && !value) {
      return { valid: false, error: `${this.name} is required` };
    }
    for (const validator of this.validators) {
      const result = validator(value);
      if (!result.valid) return result;
    }
    return { valid: true, error: null };
  }
}
 
function createContactForm() {
  const fields = [
    new FormField("name", "text", { required: true }),
    new FormField("email", "email", {
      required: true,
      validators: [
        (val) => ({
          valid: val.includes("@"),
          error: "Invalid email format"
        })
      ]
    }),
    new FormField("message", "textarea", {
      required: true,
      validators: [
        (val) => ({
          valid: val.length >= 10,
          error: "Message must be at least 10 characters"
        })
      ]
    }),
    new FormField("phone", "tel", { required: false })
  ];
 
  return {
    fields,
    validateAll(data) {
      const errors = {};
      for (const field of fields) {
        const result = field.validate(data[field.name]);
        if (!result.valid) errors[field.name] = result.error;
      }
      return {
        valid: Object.keys(errors).length === 0,
        errors
      };
    }
  };
}
 
const form = createContactForm();
const result = form.validateAll({
  name: "Alice",
  email: "invalid",
  message: "Hi",
  phone: ""
});
 
console.log(result);
// { valid: false, errors: { email: "Invalid email format", message: "Message must be at least 10 characters" } }
Rune AI

Rune AI

Key Insights

  • Object literals: Best for one-off objects like config, options, and data maps
  • Constructor functions: The pre-ES6 way to create reusable object blueprints with shared prototype methods
  • ES6 classes: Cleaner syntax over constructors with built-in extends, static methods, and enforced new
  • Object.create: Direct prototype chain control without constructors, useful for delegation patterns
  • Factory functions: Return objects from regular functions, enabling true private data via closures
RunePowered by Rune AI

Frequently Asked Questions

Which object creation method should beginners learn first?

Start with object literals because they are the simplest and most frequently used pattern in JavaScript. Object literals cover most everyday use cases, from storing configuration data to grouping related [variables](/tutorials/programming-languages/javascript/js-variables-guide-how-to-declare-and-use-them). Once you are comfortable with literals, learn classes for creating reusable blueprints.

Are ES6 classes real classes like in Java or Python?

No. JavaScript classes are syntactic sugar over the existing prototype-based [inheritance](/tutorials/programming-languages/javascript/how-prototypal-inheritance-works-in-javascript) system. Under the hood, a class declaration creates a constructor function and attaches methods to its prototype. The `class` keyword makes the syntax cleaner and enforces the use of `new`, but the underlying mechanism remains prototype-based.

When should I use a factory function instead of a class?

Use factory functions when you need true private data (via closures), when you want to return different object shapes based on conditions, or when you want to avoid the complexity of `this` binding. Factory functions are also useful when you do not need `instanceof` checks or prototype-based method sharing.

Can I mix different object creation methods in one project?

Yes, and most real projects do. You might use object literals for configuration, classes for data models, and factory functions for utility builders. The key is consistency within each category: pick one pattern for a given use case and stick with it across similar scenarios.

What is the performance difference between object literals and classes?

For creating a few objects, the difference is negligible. When creating thousands of instances, classes (and constructor functions) are more memory-efficient because methods live on the prototype and are shared across all instances. Object literals and factory functions create separate method copies for each instance, using more memory.

Conclusion

JavaScript gives you multiple tools for creating objects, and each one serves a distinct purpose. Object literals handle simple data grouping, constructors and classes provide reusable blueprints with shared methods, Object.create gives you fine-grained prototype control, and factory functions deliver true encapsulation through closures. Choosing the right pattern depends on whether you need reusability, privacy, inheritance, or simplicity.