jsguides

Object.setPrototypeOf()

setPrototypeOf(obj, prototype)

The Object.setPrototypeOf() method sets the prototype of an object to another object or null. This method changes the object’s internal [[Prototype]] property, modifying its prototype chain at runtime. While powerful, frequent prototype changes can impact performance.

Syntax

Object.setPrototypeOf(obj, prototype)

Parameters

ParameterTypeDescription
objobjectThe object whose prototype to set
prototypeobject | nullThe new prototype for the object

Both arguments are required — passing a non-object as the target throws a TypeError, and the second argument must be an object or null. The method returns the same object reference that was passed in, which means you can chain calls or assign the result directly.

Examples

Setting a prototype on a new object

const animal = {
  speak() {
    return `${this.name} makes a sound`;
  }
};

const dog = { name: 'Rex' };
Object.setPrototypeOf(dog, animal);

console.log(dog.speak()); // "Rex makes a sound"

The dog object now inherits speak() from animal through the prototype chain while keeping its own name property. Notice that setPrototypeOf layers inheritance on top of existing properties rather than overwriting them. The next example demonstrates this with an object that already carries its own data before the prototype is attached.

Changing prototype of existing object

const proto1 = { a: 1 };
const obj = { b: 2 };

Object.setPrototypeOf(obj, proto1);
console.log(obj.a); // 1
console.log(obj.b); // 2

Here the object’s own property b is preserved alongside the inherited property a from proto1. You can verify the current prototype at any time with Object.getPrototypeOf(). The next example shows what happens when you strip the prototype entirely by passing null — producing an object with no inherited methods at all.

Setting prototype to null

const obj = { x: 1 };
Object.setPrototypeOf(obj, null);

console.log(Object.getPrototypeOf(obj)); // null
console.log(obj.toString); // undefined - no inherited methods

A null-prototype object is a bare dictionary — useful when you need a key-value store with zero risk of inherited property collisions or prototype pollution attacks. The more common use case runs in the opposite direction: adding behaviour to an existing object, as the next example demonstrates with a single-inheritance mixin pattern.

Mixin-like behavior

const canFly = {
  fly() {
    return `${this.name} is flying`;
  }
};

const bird = { name: 'Sparrow' };
Object.setPrototypeOf(bird, canFly);

console.log(bird.fly()); // "Sparrow is flying"

The mixin approach works well for attaching a single capability to an object, but real applications often need to layer several behaviours — flying, swimming, walking — onto one instance. The next example extends the pattern by iterating over an array of mixin objects and attaching each one as the prototype in turn.

Common Patterns

Dynamic object composition

function extendObject(target, mixins) {
  for (const mixin of mixins) {
    Object.setPrototypeOf(target, mixin);
  }
  return target;
}

const canSwim = { swim() { return 'swimming'; } };
const canWalk = { walk() { return 'walking'; } };

const amphibian = extendObject({ name: 'Frog' }, [canSwim, canWalk]);
console.log(amphibian.swim()); // "swimming"
console.log(amphibian.walk()); // "walking"

This loop-based composition approach is flexible and reads clearly, but each call to setPrototypeOf inside the loop replaces the prototype wholesale. Repeated swaps carry a real cost inside the JavaScript engine, as the next section explains. Understanding what happens under the hood will help you decide when the pattern is acceptable and when it is not.

Performance note

Changing an object’s prototype after creation is one of the slowest operations in JavaScript. The JavaScript engine optimises property lookups using hidden classes (also called shapes or maps internally). When you change a prototype at runtime, the engine must rebuild those internal structures for every object in the affected chain, deoptimising any code that was already compiled for the original prototype.

The advice from V8 and SpiderMonkey engineers is consistent: set the prototype at object creation time using Object.create(proto) or class syntax, and avoid setPrototypeOf entirely in hot code paths.

// Fast — prototype set at creation
const obj = Object.create(myProto);

// Slow — prototype changed after creation
const obj2 = {};
Object.setPrototypeOf(obj2, myProto); // triggers deoptimisation

The two-line comparison above captures why engine teams recommend Object.create over setPrototypeOf: one path compiles once, the other deoptimises on every call. Even during application startup, the hidden-class rebuild cost adds up across large object graphs. Another constraint worth knowing is that frozen and sealed objects reject prototype changes outright.

Throwing on sealed or frozen objects

setPrototypeOf throws a TypeError if the target object is non-extensible — for example, if it has been frozen with Object.freeze() or sealed with Object.seal().

const obj = Object.freeze({ x: 1 });
Object.setPrototypeOf(obj, {}); // TypeError: Cannot set property of a non-extensible object

Always check extensibility before changing prototypes on objects you did not create yourself.

setPrototypeOf() vs Object.create()

Object.create(proto) creates a new object with proto as its prototype — use this when creating objects. setPrototypeOf(obj, proto) changes the prototype of an existing object — use this only when you must modify an object after it has already been created. In nearly all cases, Object.create is the right tool.

Relation to proto

Object.setPrototypeOf(obj, proto) is the standard replacement for the deprecated obj.__proto__ = proto assignment. Both accomplish the same thing, but the standard method should be preferred in all new code.

Circular prototype chains

Object.setPrototypeOf() throws a TypeError if the operation would create a circular prototype chain. JavaScript engines detect this and prevent it, since a circular chain would cause infinite loops in any property lookup that walks the prototype chain.

See Also