Object.seal()
ES5+Seals an object and returns it: no properties can be added or removed, and existing properties become non-configurable, but their values remain writable. It sits between preventExtensions (loosest) and freeze (strictest) on the mutability spectrum — a fixed shape with changeable contents.
Syntax
Object.seal(obj)Parameters
obj Object The object to seal
Return Value
The sealed object
Examples
const obj = { name: 'John' };
Object.seal(obj);
obj.name = 'Jane'; // 가능
obj.age = 30; // 무시됨
console.log(obj); 📌 When to Use
Use Object.seal() when an object's shape is part of its contract but its data must keep changing. Sealing a session object, a game-entity record, or a form-state model guarantees that typos like session.tokn = x fail (loudly in strict mode) instead of quietly creating a stray property, while legitimate updates to existing fields continue to work. That property makes seal a lightweight runtime schema check in plain JavaScript projects without TypeScript: construct the object with every field it will ever have (using null placeholders where needed), seal it, and any code writing to a misspelled or unplanned key is caught immediately. It also helps readers and JavaScript engines alike: a sealed object visibly commits to a stable set of keys, which matches how optimizers prefer objects to behave anyway. Choose freeze instead when values must not change either; choose preventExtensions alone when you want to block additions but still allow deletions during a migration or cleanup phase. As with freeze, sealing is shallow — nested objects keep their own, unsealed behavior unless treated separately.
⚠️ Common Mistakes
Sealing objects before all fields exist - because sealed objects reject new properties, a lazily-added field like user.lastLogin set after sealing is silently dropped (or throws in strict mode). Declare every property up front, using null or undefined placeholders, and only then seal.
Assuming seal blocks all Object.defineProperty() changes - sealing makes properties non-configurable, so redefining enumerability or converting a data property into an accessor throws, but plain value updates through defineProperty still succeed for writable properties. Seal flips exactly one descriptor bit: configurable.
Confusing seal with freeze - sealed objects CAN have their property values changed, frozen objects cannot.
Expecting Object.seal() to be deep - like freeze, it only affects the top-level object, not nested objects.
Not knowing that deletion attempts fail silently in non-strict mode - use strict mode to catch these errors.
✅ Best Practices
Turn seal into a cheap schema guard in plain JavaScript: initialize objects with their complete field set in a factory function and return Object.seal(obj) - every misspelled assignment then throws in strict mode instead of creating a phantom property that serializers happily leak into payloads.
Document the choice in code: a short comment like "sealed: fixed shape, mutable values" next to the seal call saves the next developer from re-deriving why additions fail while updates work - the seal versus freeze distinction is a recurring source of team confusion.
Use Object.seal() for data transfer objects (DTOs) where the structure is fixed but values may be updated.
Combine with TypeScript interfaces for compile-time and runtime property protection.
Use Object.seal() over Object.freeze() when you need a mutable cache with a fixed key set.
⚡ Performance Notes
Object.seal() performs a one-time O(n) pass marking each own property non-configurable and the object non-extensible. Afterward, reads and writes to existing properties run at normal speed — in some engines sealed objects are actually friendlier to the optimizer than ordinary ones, because their shape is guaranteed stable and hidden-class transitions stop occurring. The cost profile mirrors freeze: seal once at object creation, not repeatedly in hot loops, and avoid sealing objects churned out thousands of times per second unless the factory reuses one shape. Beware speculative writes in strict mode: attempts to add properties to sealed objects throw, and exception creation is expensive, so code that probes sealed objects with try/catch around writes will crawl. Object.isSealed() checks are cheap for the small objects typically used in guards and assertions.
🌍 Real World Example
User Session Manager with Fixed Properties
This session manager demonstrates the exact contract seal provides: a factory declares the complete session shape up front — token and expiry start as null placeholders — and returns a sealed object. Later code freely updates authentication state, but an attempt to bolt on newProperty vanishes silently (in strict mode it would throw), and delete cannot remove userId. The closing isSealed and isFrozen checks make the mutability level explicit. This shape-stable-but-mutable pattern is common in auth layers, connection-state objects, and game entities.
function createSession(userId) {
const session = {
userId,
token: null,
expiresAt: null,
lastActivity: Date.now(),
isAuthenticated: false
};
// Seal the session - no new properties, but values can change
return Object.seal(session);
}
const session = createSession('user123');
// Updating values works fine
session.token = 'abc123xyz';
session.isAuthenticated = true;
session.expiresAt = Date.now() + 3600000;
console.log(session.isAuthenticated); // true
// Adding new properties fails silently (or throws in strict mode)
session.newProperty = 'test';
console.log(session.newProperty); // undefined
// Deleting properties also fails
delete session.userId;
console.log(session.userId); // 'user123' (still exists)
// Check status
console.log(Object.isSealed(session)); // true
console.log(Object.isFrozen(session)); // false (values can still change)