jsguides

Object.seal()

seal(obj)

The Object.seal() method seals an object, preventing new properties from being added to it and marking all existing properties as non-configurable. However, existing properties can still be modified (unlike freeze).

Syntax

Object.seal(obj)

Parameters

ParameterTypeDefaultDescription
objobjectThe object to seal.

Return Value

Returns the object that was passed in (not a copy).

The return value is the original object reference — Object.seal(obj) === obj is always true. Since sealing modifies the object in place rather than creating a wrapper or proxy, any existing references to the object immediately reflect the sealed state.

Examples

Basic seal usage

const user = {
  name: 'Alice',
  age: 30
};

Object.seal(user);

// This works - changing existing property values is allowed
user.age = 31;
console.log(user.age);  // 31

// These fail - can't add or remove properties
user.email = 'alice@example.com';  // Ignored
delete user.name;                   // Ignored

Object.isSealed(user);  // true

Sealed objects let you modify existing values but prevent structural changes. This is useful when you want to lock down an object’s shape while allowing its data to evolve. Use Object.isSealed() to check whether an object has been sealed — it returns true for sealed objects and also for frozen objects, since freezing implies sealing.

Freeze vs seal comparison

const frozen = Object.freeze({ value: 1 });
const sealed = Object.seal({ value: 1 });

// Frozen: can't change values
frozen.value = 2;
console.log(frozen.value);  // 1

// Seal: can change values
sealed.value = 2;
console.log(sealed.value);  // 2

// Both prevent extensions
frozen.newProp = 'x';
sealed.newProp = 'x';

console.log(frozen.newProp);  // undefined
console.log(sealed.newProp); // undefined

The key difference: freeze locks values too, while seal only locks structure. Choose seal when property names are part of a contract but values change over time. Choose freeze when the entire object — keys and values — must stay constant, such as for configuration constants or enum-like objects.

Common Patterns

Protecting state objects in classes:

class Counter {
  constructor() {
    this._count = 0;
    Object.seal(this);
  }

  increment() {
    this._count++;  // Works - modifying value
    // Can't add new properties - sealed
  }
}

Sealing the instance inside the constructor prevents callers from accidentally attaching new properties after the object is created. Combined with private fields (#field), this gives you both structural and access-level guarantees for class instances.

Caveats

Nested objects can still be modified:

const obj = {
  nested: { value: 1 }
};

Object.seal(obj);
obj.nested.value = 2;  // This works!

console.log(obj.nested.value);  // 2

Sealing doesn’t affect nested objects. Each nested object is a separate reference with its own property descriptors. To fully seal an object graph, you must traverse the structure and call Object.seal() on every nested object individually — seal is shallow by design.

What sealing does internally

Sealing an object does three things: it marks the object as non-extensible (no new properties can be added), and it marks every existing own property as non-configurable (properties cannot be deleted or have their descriptor changed). Existing writable properties remain writable — you can still update their values. This is why Object.seal() sits between “no restrictions” and Object.freeze() on the immutability spectrum.

Object.isSealed()

Object.isSealed(obj) returns true if an object is sealed — either because Object.seal() was called on it, or because it is non-extensible and all its properties are non-configurable. Frozen objects are also sealed, so Object.isSealed(Object.freeze({})) returns true.

const obj = { x: 1 };
console.log(Object.isSealed(obj)); // false
Object.seal(obj);
console.log(Object.isSealed(obj)); // true

Strict mode behaviour

In strict mode, attempting to add a property to a sealed object throws a TypeError. In non-strict (sloppy) mode, the assignment is silently ignored. To ensure consistent and predictable behaviour, always run code in strict mode when working with sealed or frozen objects.

'use strict';
const obj = Object.seal({ x: 1 });
obj.y = 2; // TypeError: Cannot add property y, object is not extensible

Sealing is irreversible

Once an object is sealed, it cannot be unsealed. There is no Object.unseal(). If you need a mutable copy, create one before sealing — for example with Object.assign({}, original) or { ...original }.

When seal() is the better middle ground

Object.seal() is useful when you want to lock an object’s shape without freezing its data. That makes it a practical middle ground for state objects, configuration snapshots, and APIs that should not gain or lose properties after setup. You can still update the values that already exist, but the object can no longer grow new fields or shed old ones.

This is especially handy when property names are part of a contract. By sealing the object, you make it easier to reason about the available keys and reduce the chance that later code will quietly attach something unexpected. It is less strict than freezing, but that flexibility is exactly what makes it useful.

See Also