Learn JavaScript

Object.setPrototypeOf() - метод для изменения прототипа объекта в JavaScript

Object.setPrototypeOf() меняет прототип объекта.

Это довольно редкий метод. Его полезно знать, но в обычном коде им пользуются нечасто, потому что менять прототип уже созданного объекта обычно хуже, чем сразу создать объект с нужным прототипом.

Синтаксис

Object.setPrototypeOf(obj, prototype)

obj - объект, у которого нужно изменить прототип.

prototype - новый прототип. Это должен быть объект или null.

Что делает Object.setPrototypeOf()

Метод меняет прототип уже существующего объекта.

const animal = {
  speak() {
    console.log("sound");
  },
};

const dog = {
  name: "Rex",
};

Object.setPrototypeOf(dog, animal);

dog.speak(); // "sound"

После этого dog начинает наследоваться от animal.

Что возвращает метод

Object.setPrototypeOf() возвращает тот же самый объект.

const obj = {};
const proto = {};

const result = Object.setPrototypeOf(obj, proto);

console.log(result === obj); // true

Это важно: метод не создаёт новый объект, а меняет существующий.

Как проверить результат

После изменения прототип можно проверить через Object.getPrototypeOf().

const proto = { role: "user" };
const obj = {};

Object.setPrototypeOf(obj, proto);

console.log(Object.getPrototypeOf(obj) === proto); // true
console.log(obj.role); // "user"

Можно установить null

Новым прототипом может быть не только объект, но и null.

const user = {
  name: "Anna",
};

Object.setPrototypeOf(user, null);

console.log(Object.getPrototypeOf(user)); // null

После этого объект перестаёт наследоваться от Object.prototype.

Object.setPrototypeOf() и Object.create()

Эти методы похожи по теме, но работают по-разному.

Object.create():

  • сразу создаёт новый объект с нужным прототипом

Object.setPrototypeOf():

  • берёт уже существующий объект
  • и меняет его прототип потом
const proto = { role: "user" };

const a = Object.create(proto);
const b = {};

Object.setPrototypeOf(b, proto);

Если объект только создаётся, чаще предпочитают Object.create().

Почему метод используют редко

Изменение прототипа у уже созданного объекта обычно считается не лучшей практикой.

Причины простые:

  • такой код сложнее читать
  • он может быть медленнее
  • чаще удобнее сразу создать объект правильно

Поэтому Object.setPrototypeOf() обычно оставляют для специальных случаев, а не для повседневного кода.

Когда метод выбрасывает ошибку

Если вторым аргументом передать не объект и не null, будет TypeError.

const obj = {};

Object.setPrototypeOf(obj, 123); // TypeError
Object.setPrototypeOf(obj, "proto"); // TypeError

null и undefined как target

Если первым аргументом передать null или undefined, тоже будет ошибка.

Object.setPrototypeOf(null, {});      // TypeError
Object.setPrototypeOf(undefined, {}); // TypeError

Примитивы

Если первым аргументом передать примитив, метод просто вернёт это значение обратно.

console.log(Object.setPrototypeOf(123, {}));     // 123
console.log(Object.setPrototypeOf("hello", {})); // "hello"

Но полезной смены прототипа тут, конечно, не происходит.

Поэтому на практике метод используют именно с объектами.

Нерасширяемые объекты

Если объект уже нельзя расширять, смена прототипа на другой обычно вызовет ошибку.

const base = {};
const obj = Object.create(base);

Object.preventExtensions(obj);

Object.setPrototypeOf(obj, {}); // TypeError

Если же попытаться установить тот же самый прототип, ошибка обычно не возникает.

Object.setPrototypeOf() не копирует свойства

Метод не переносит свойства из нового прототипа в сам объект.

Он только меняет цепочку наследования.

const base = { role: "user" };
const obj = {};

Object.setPrototypeOf(obj, base);

console.log(obj.role); // "user"
console.log(obj.hasOwnProperty("role")); // false

Свойство role здесь пришло из прототипа, а не стало собственным свойством obj.

Object.setPrototypeOf() и isPrototypeOf()

После изменения прототипной цепочки это легко проверить.

const base = {};
const obj = {};

Object.setPrototypeOf(obj, base);

console.log(base.isPrototypeOf(obj)); // true

То есть один метод меняет цепочку, а другой помогает проверить результат.

Объекты без прототипа

Метод можно использовать и с объектами, созданными через Object.create(null).

const dict = Object.create(null);
const proto = { type: "base" };

Object.setPrototypeOf(dict, proto);

console.log(Object.getPrototypeOf(dict) === proto); // true

Это редкий, но возможный сценарий.

Когда это полезно

Object.setPrototypeOf() удобно использовать, когда:

  • нужно явно поменять прототип уже существующего объекта
  • вы работаете с метапрограммированием или низкоуровневой логикой объектов
  • объект нельзя пересоздать, а прототипную цепочку нужно изменить

Но в обычном приложении такие случаи встречаются редко.

Частые ошибки

Думать, что метод создаёт новый объект

const obj = {};
const proto = {};

const result = Object.setPrototypeOf(obj, proto);

console.log(result === obj); // true

Метод работает с тем же самым объектом.

Путать смену прототипа и копирование свойств

const proto = { role: "user" };
const obj = {};

Object.setPrototypeOf(obj, proto);

console.log(obj.role); // "user"
console.log(obj.hasOwnProperty("role")); // false

Свойство доступно через прототип, но не копируется в сам объект.

Использовать метод там, где проще Object.create()

const proto = { role: "user" };
const obj = {};

Object.setPrototypeOf(obj, proto);

Если объект только создаётся, обычно проще и понятнее сразу написать:

const obj = Object.create(proto);

Быстрая памятка

  • Object.setPrototypeOf() меняет прототип уже существующего объекта
  • метод возвращает тот же самый объект
  • новый прототип должен быть объектом или null
  • метод не копирует свойства, а меняет только цепочку наследования
  • в обычном коде чаще предпочитают Object.create()

Коротко

Object.setPrototypeOf() - это способ вручную поменять прототип объекта. Главное, что стоит запомнить: метод не создаёт новый объект и не копирует свойства, а просто перестраивает прототипную цепочку, поэтому использовать его стоит только там, где это действительно нужно.