Object.setPrototypeOf()

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

Застереження: Зміна властивості об'єкта [[Prototype]] за природою того, як сучасні рушії JavaScript оптимізують доступ до властивостей, є дуже повільною операцією у кожному переглядачі та рушії JavaScript. До того ж, ефект від зміни наслідування є неочевидним та обширним, і не обмежується лише часом, витраченим на інструкцію Object.setPrototypeOf(...), а може стосуватися будь-якого коду, що звертається до будь-якого об'єкта, чия властивість [[Prototype]] була змінена.

Оскільки ця функціональність є частиною мови, тягар її ефективної (в ідеалі) реалізації досі лежить на розробниках рушіїв. Поки розробники рушіїв не вирішать цю проблему, якщо вам важлива продуктивність, вам варто уникати присвоювати [[Prototype]] об'єкта. Замість цього, створіть новий об'єкт з бажаним значенням [[Prototype]], використовуючи Object.create().

Синтаксис

Object.setPrototypeOf(obj, prototype)

Параметри

obj
Об'єкт, прототипу якого треба присвоїти значення.
prototype
Новий прототип об'єкта (об'єкт або null).

Значення, що повертається

Вказаний об'єкт.

Опис

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

Метод Object.setPrototypeOf() присутній у специфікації ECMAScript 2015. Він, загалом, вважається правильним способом встановлювати прототип об'єкта, у порівнянні з більш суперечливою властивістю Object.prototype.__proto__.

Приклади

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

Поліфіл

Використовуючи більш стару властивість Object.prototype.__proto__, ми легко можемо визначити метод Object.setPrototypeOf, якщо він досі не є доступним:

if (!Object.setPrototypeOf) {
    // Працює лише у Chrome та FireFox, не працює у IE:
     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 - Недостатньо аргументів');
  }
  if (typeof oProto !== 'object' && typeof oProto !== 'string') {
    throw new TypeError("другий аргумент Object.appendChain повинен бути об'єктом або рядком");
  }

  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('Кіт');

console.log(oCat.isMammal); // 'так'

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

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

console.log(oCat.breathing); // 'так'

Другий приклад: Перетворення простого значення на екземпляр свого конструктора та додавання його ланцюжка до прототипу

function MySymbol() {
  this.isSymbol = 'так';
}

var nPrime = 17;

console.log(typeof nPrime); // 'number'

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

console.log(oPrime); // '17'
console.log(oPrime.isSymbol); // 'так'
console.log(typeof oPrime); // 'object'

Третій приклад: Додавання ланцюжка до об'єкта Function.prototype та додавання нової функції до цього ланцюжка

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

var george = Object.appendChain(new Person('Георгій'),
                                'console.log("Всім привіт!!");');

console.log(george.identity); // 'Георгій'
george(); // 'Всім привіт!!'

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

Специфікація Статус Коментар
ECMAScript Latest Draft (ECMA-262)
The definition of 'Object.setPrototypeOf' in that specification.
Draft
ECMAScript 2015 (6th Edition, ECMA-262)
The definition of 'Object.setPrototypeOf' in that specification.
Standard Початкове визначення.

Сумісність з веб-переглядачами

Update compatibility data on GitHub
DesktopMobileServer
ChromeEdgeFirefoxInternet ExplorerOperaSafariAndroid webviewChrome for AndroidFirefox for AndroidOpera for AndroidSafari on iOSSamsung InternetNode.js
setPrototypeOfChrome Full support 34Edge Full support 12Firefox Full support 31IE Full support 11Opera Full support YesSafari Full support 9WebView Android Full support 37Chrome Android Full support 34Firefox Android Full support 31Opera Android Full support YesSafari iOS Full support YesSamsung Internet Android Full support Yesnodejs Full support 0.12

Legend

Full support  
Full support

Див. також