Object.setPrototypeOf()

Предупреждение: Изменение прототипа [[Prototype]] объекта является, по самой природе оптимизации доступа к свойствам в современных движках JavaScript, очень медленной операцией, это справедливо для любого браузера и движка JavaScript. Изменение прототипов очень тонко и обширно влияет на производительность, причём это влияние не ограничивается просто временем, проведённым внутри метода Object.setPrototypeOf(), оно может распространяться на любой код, который имеет доступ к любому объекту, чей прототип [[Prototype]] был изменён. Если вы заботитесь о производительности, вы никогда не должны изменять прототип [[Prototype]] объекта. Вместо этого создайте объект с нужным прототипом [[Prototype]], с помощью метода Object.create().

Сводка

Метод Object.setPrototypeOf() устанавливает прототип (то есть, внутреннее свойство [[Prototype]]) указанного объекта в другой объект или null.

Синтаксис

Object.setPrototypeOf(obj, prototype);

Параметры

obj
Объект, которому устанавливается прототип.
prototype
Новый прототип объекта (объект или null).

Описание

Выкидывает исключение TypeError, если объект, чей прототип [[Prototype]] является не расширяемым, согласно методу Object.isExtensible(). Не делает ничего, если параметр prototype не является объектом или значением null (то есть, является числом, строкой, логическим значением или undefined). В противном случае метод устанавливает прототип [[Prototype]] объекта obj в новое значение.

Примеры

var dict = Object.setPrototypeOf({}, null);

Полифил

Используя старое свойство Object.prototype.__proto__, мы можем легко определить Object.setPrototypeOf(), если он ещё не доступен:

if (!Object.setPrototypeOf) {
     Object.prototype.setPrototypeOf = function(obj, proto) {
         if(obj.__proto__) {
             obj.__proto__ = proto;
             return obj;
         } else {
             // Если нужно будет определить прототип у Object.create(null) объекта
             var Fn = function() {
                 for (var key in obj) { //Если в объект уже были определены некоторые свойства
                     Object.defineProperty(this, key, {
                         value: obj[key],
                     });
                 }
             };
             Fn.prototype = proto;
             return new Fn();
         }
     }
}

Добавление цепочки прототипов

Сочетание метода Object.getPrototypeOf() и свойства Object.prototype.__proto__ позволяет добавить целую цепочку прототипов к новому прототипу объекта:

/**
*** Object.appendChain(@object, @prototype)
*
* Присоединяет первый неродной прототип цепочки к новому прототипу.
* Возвращает @object (если он был примитивным значением, оно будет преобразовано в объект).
*
*** Object.appendChain(@object [, "@arg_name_1", "@arg_name_2", "@arg_name_3", "..."], "@function_body")
*** Object.appendChain(@object [, "@arg_name_1, @arg_name_2, @arg_name_3, ..."], "@function_body")
*
* Присоединяет первый не родной прототип цепочки к родному объекту Function.prototype, затем присоединяет
* new Function(["@arg"(s)], "@function_body") к этой цепочке.
* Возвращает функцию.
*
**/

Object.appendChain = function(oChain, oProto) {
  if (arguments.length < 2) {
    throw new TypeError('Object.appendChain - Not enough arguments');
  }
  if (typeof oProto === 'number' || typeof oProto === 'boolean') {
    throw new TypeError('second argument to Object.appendChain must be an object or a string');
  }

  var oNewProto = oProto,
      oReturn = o2nd = oLast = oChain instanceof this ? oChain : new oChain.constructor(oChain);

  for (var o1st = this.getPrototypeOf(o2nd);
    o1st !== Object.prototype && o1st !== Function.prototype;
    o1st = this.getPrototypeOf(o2nd)
  ) {
    o2nd = o1st;
  }

  if (oProto.constructor === String) {
    oNewProto = Function.prototype;
    oReturn = Function.apply(null, Array.prototype.slice.call(arguments, 1));
    this.setPrototypeOf(oReturn, oLast);
  }

  this.setPrototypeOf(o2nd, oNewProto);
  return oReturn;
}

Использование

Первый пример: присоединение цепочки к прототипу

function Mammal() {
  this.isMammal = 'да';
}

function MammalSpecies(sMammalSpecies) {
  this.species = sMammalSpecies;
}

MammalSpecies.prototype = new Mammal();
MammalSpecies.prototype.constructor = MammalSpecies;

var oCat = new MammalSpecies('Felis');

alert(oCat.isMammal); // 'да'

function Animal() {
  this.breathing = 'да';
}

Object.appendChain(oCat, new Animal());

alert(oCat.breathing); // 'да'

Второй пример: преобразование примитивного значения в экземпляр его конструктора и присоединение его цепочки к прототипу

function Symbol() {
  this.isSymbol = 'да';
}

var nPrime = 17;

alert(typeof nPrime); // 'number'

var oPrime = Object.appendChain(nPrime, new Symbol());

alert(oPrime); // '17'
alert(oPrime.isSymbol); // 'да'
alert(typeof oPrime); // 'object'

Третий пример: присоединение цепочки к объекту Function.prototype и новой функции к этой цепочке

function Person(sName) {
  this.identity = sName;
}

var george = Object.appendChain(new Person('Георг'),
                                'alert("Привет, парни!!");');

alert(george.identity); // 'Георг'
george(); // 'Привет, парни!!'

Спецификации

Specification
ECMAScript Language Specification (ECMAScript)
# sec-object.setprototypeof

Совместимость с браузерами

BCD tables only load in the browser

Смотрите также