Object.create()
Метод Object.create()
створює новий об'єкт, використовуючи існуючий об'єкт як прототип для новоствореного об'єкта.
The source for this interactive example is stored in a GitHub repository. If you'd like to contribute to the interactive examples project, please clone https://github.com/mdn/interactive-examples and send us a pull request.
Синтаксис
Object.create(proto, [propertiesObject])
Параметри
proto
- Об'єкт, що має бути прототипом для новоствореного об'єкта.
propertiesObject
Optional- Якщо вказаний та не дорівнює
undefined
, об'єкт, чиї власні перелічувані властивості (тобто, властивості, визначені на самому об'єкті, а не перелічувані властивості, отримані через ланцюжок прототипів) визначають дескриптори властивостей, що мають бути додані до новоствореного об'єкта, з відповідними іменами властивостей. Ці властивості відповідають другому аргументуObject.defineProperties()
.
Значення, що повертається
Новий об'єкт з зазначеним прототипом та властивостями.
Винятки
Виняток TypeError
, якщо параметр propertiesObject
дорівнює null
або це об'єкт, що не є обгорткою простого типу.
Приклади
Класичне наслідування через Object.create()
Нижче наведено приклад використання Object.create()
для отримання класичного наслідування. Це приклад одиночного наслідування, єдиного, яке підтримує JavaScript.
// Shape - батьківський клас
function Shape() {
this.x = 0;
this.y = 0;
}
// метод батьківського класу
Shape.prototype.move = function(x, y) {
this.x += x;
this.y += y;
console.info('Фігуру переміщено.');
};
// Rectangle - дочірній клас
function Rectangle() {
Shape.call(this); // виклик батьківського конструктора.
}
// дочірній клас розширює батьківській клас
Rectangle.prototype = Object.create(Shape.prototype);
//Якщо ви не призначите Object.prototype.constructor значення Rectangle,
//він візьме prototype.constructor класу Shape (батьківського).
//Щоб уникнути цього, ми призначаємо prototype.constructor значення Rectangle (дочірній).
Rectangle.prototype.constructor = Rectangle;
var rect = new Rectangle();
console.log('Чи є rect екземпляром Rectangle?', rect instanceof Rectangle); // true
console.log('Чи є rect екземпляром Shape?', rect instanceof Shape); // true
rect.move(1, 1); // Виводить 'Фігуру переміщено.'
Якщо ви бажаєте успадкуватись від кількох об'єктів, можна використати домішки.
function MyClass() {
SuperClass.call(this);
OtherSuperClass.call(this);
}
// успадкувати від одного класу
MyClass.prototype = Object.create(SuperClass.prototype);
// змішати з іншим
Object.assign(MyClass.prototype, OtherSuperClass.prototype);
// перепризначити конструктор
MyClass.prototype.constructor = MyClass;
MyClass.prototype.myMethod = function() {
// зробити щось
};
Метод Object.assign()
копіює властивості з прототипу OtherSuperClass у прототип MyClass, роблячи їх доступними для усіх екземплярів MyClass. Object.assign()
був запроваджений у ES2015 і може бути відтворений поліфілом. Якщо необхідна підтримка більш старих переглядачів, можна використати jQuery.extend()
або _.assign()
.
Використання аргументу propertiesObject
у Object.create()
var o;
// створити об'єкт з прототипом null
o = Object.create(null);
o = {};
// є еквівалентом:
o = Object.create(Object.prototype);
// У цьому прикладі ми створюємо об'єкт з парою
// зразкових властивостей. (Зауважте, що другий параметр
// встановлює ключі у відповідності до *дескрипторів властивості*.)
o = Object.create(Object.prototype, {
// foo - це звичайна властивість-значення
foo: {
writable: true,
configurable: true,
value: 'hello'
},
// bar - це властивість-аксесор (має гетер та сетер)
bar: {
configurable: false,
get: function() { return 10; },
set: function(value) {
console.log('Призначення `o.bar` значення', value);
}
/* з аксесорами ES2015 наш код може виглядати так
get() { return 10; },
set(value) {
console.log('Призначення `o.bar` значення', value);
} */
}
});
function Constructor() {}
o = new Constructor();
// є еквівалентом:
o = Object.create(Constructor.prototype);
// Звісно, якщо у функції Constructor присутній
// код ініціалізації,
// Object.create() не може його відобразити
// Створити новий об'єкт, чиїм прототипом є новий, порожній
// об'єкт, та додати єдину властивість 'p' зі значенням 42.
o = Object.create({}, { p: { value: 42 } });
// за замовчуванням властивості НЕДОСТУПНІ для запису,
// переліку чи налаштування:
o.p = 24;
o.p;
// 42
o.q = 12;
for (var prop in o) {
console.log(prop);
}
// 'q'
delete o.p;
// false
// щоб визначити властивість у ES3
o2 = Object.create({}, {
p: {
value: 42,
writable: true,
enumerable: true,
configurable: true
}
});
/* є еквівалентом:
o2 = Object.create({p: 42}) */
Користувацькі та нульові об'єкти
Новий об'єкт, створений на основі користувацького об'єкта (особливо об'єкт, створений на основі об'єкта null
, який по суті є користувацьким об'єктом, що НЕ МАЄ членів), може поводитись неочікувано. Особливо під час налагодження, оскільки звичайні функції утиліт для перетворення/видалення об'єктних властивостей можуть генерувати помилки або просто втрачати інформацію (особливо якщо використовують перехоплювачі помилок, що ігнорують помилки). Наприклад, ось два об'єкти:
oco = Object.create( {} ); // створити нормальний об'єкт
ocn = Object.create( null ); // створити "нульовий" об'єкт
> console.log(oco) // {} -- Виглядає нормально
> console.log(ocn) // {} -- Поки що теж виглядає нормально
oco.p = 1; // створити просту властивість на нормальному об'єкті
ocn.p = 0; // створити просту властивість на "нульовому" об'єкті
> console.log(oco) // {p: 1} -- Досі виглядає нормально
> console.log(ocn) // {p: 0} -- Теж виглядає нормально. АЛЕ СТРИВАЙТЕ...
Як показано вище, поки що все виглядає нормальним. Однак, при спробі використати ці об'єкти, їхні відмінності швидко стають очевидними:
> "oco is: " + oco // виводить "oco is: [object Object]"
> "ocn is: " + ocn // викидає помилку: Cannot convert object to primitive value
Перевірка лише декількох з багатьох базових вбудованих функцій більш чітко демонструє величину проблеми:
> alert(oco) // виводить [object Object]
> alert(ocn) // викидає помилку: Cannot convert object to primitive value
> oco.toString() // виводить [object Object]
> ocn.toString() // викидає помилку: ocn.toString is not a function
> oco.valueOf() // виводить {}
> ocn.valueOf() // викидає помилку: ocn.valueOf is not a function
> oco.hasOwnProperty("p") // виводить "true"
> ocn.hasOwnProperty("p") // викидає помилку: ocn.hasOwnProperty is not a function
> oco.constructor // виводить "Object() { [native code] }"
> ocn.constructor // виводить "undefined"
Як вже сказано, ці відмінності можуть швидко зробити процес відлагодження навіть простих на вигляд проблем дуже заплутаним. Наприклад:
Проста звичайна функція налагодження:
// вивести пари ключ-значення властивостей верхнього рівня наданого об'єкта
function ShowProperties(obj){
for(var prop in obj){
console.log(prop + ": " + obj[prop] + "\n" )
}
}
Не такі прості результати: (особливо якщо перехоплювач помилок сховав повідомлення про помилки)
ob={}; ob.po=oco; ob.pn=ocn; // створити складний об'єкт з наданих вище тестових об'єктів в якості значень властивостей
> ShowProperties( ob ) // вивести властивості верхнього рівня
- po: [object Object]
- Error: Cannot convert object to primitive value
Зауважте, що виводиться тільки перша властивість.
(Але якщо такий самий об'єкт був просто створений в іншому порядку -- принаймні, в деяких реалізаціях...)
ob={}; ob.pn=ocn; ob.po=oco; // створити знову такий самий об'єкт, але створити ті самі властивості в іншому порядку
> ShowProperties( ob ) // вивести властивості верхнього рівня
- Error: Cannot convert object to primitive value
Зауважте, що жодна властивість не виводиться.
Зауважте, що такий відмінний порядок може виникнути статично, через відмінний зафіксований код, як ось тут, а може й динамічно, через порядок, в якому виконуються під час запуску гілки коду з додаванням властивостей, що залежить від вхідних даних та/або випадкових змінних. Знову ж таки, реальний порядок перебору не гарантований, в якому б порядку не додавалися члени об'єкта.
Деякі рішення, що не працюють
Гарне вирішення проблеми відсутніх об'єктних методів не завжди є очевидним.
Пряме додавання відсутнього об'єктного метода зі стандартного об'єкта НЕ працює:
ocn = Object.create( null ); // створити "нульовий" об'єкт (такий самий, як і раніше)
ocn.toString = Object.toString; // оскільки йому бракує методу, призначити його прямо зі стандартного об'єкта
> ocn.toString // виводить "toString() { [native code] }" -- схоже, що відсутній метод тепер додано
> ocn.toString == Object.toString // виводить "true" -- схоже, це той самий метод, що й у стандартному об'єкті
> ocn.toString() // error: Function.prototype.toString requires that 'this' be a Function
Пряме додавання відсутнього об'єктного метода у "прототип" нового об'єкта також не працює, оскільки у нового об'єкта немає справжнього прототипа (що й є справжньою причиною УСІХ цих проблем), і його не можна додати прямо:
ocn = Object.create( null ); // створити "нульовий" об'єкт (такий самий, як і раніше)
ocn.prototype.toString = Object.toString; // Error: Cannot set property 'toString' of undefined
ocn.prototype = {}; // спробувати створити прототип
ocn.prototype.toString = Object.toString; // оскільки об'єкту бракує методу, призначити його прямо зі стандартного об'єкта
> ocn.toString() // error: ocn.toString is not a function
Додавання відсутнього об'єктного метода використанням стандартного об'єкта в якості прототипа нового об'єкта також не працює:
ocn = Object.create( null ); // створити "нульовий" об'єкт (такий самий, як і раніше)
Object.setPrototypeOf(ocn, Object); // встановити значенням прототипу нового об'єкта стандартний об'єкт
> ocn.toString() // error: Function.prototype.toString requires that 'this' be a Function
Деякі вдалі рішення
Як вже сказано, пряме додавання відсутнього об'єктного методу зі стандартного об'єкта НЕ працює. Однак, пряме додавання загального метода ПРАЦЮЄ:
ocn = Object.create( null ); // створити "нульовий" об'єкт (такий самий, як і раніше)
ocn.toString = toString; // оскільки об'єкту бракує методу, призначити його прямо з загальної версії
> ocn.toString() // виводить "[object Object]"
> "ocn is: " + ocn // виводить "ocn is: [object Object]"
ob={}; ob.pn=ocn; ob.po=oco; // створити складний об'єкт (такий самий, як і раніше)
> ShowProperties(ob) // вивести властивості верхнього рівня
- po: [object Object]
- pn: [object Object]
Однак, встановлення загального прототипу прототипом нового об'єкта працює навіть краще:
ocn = Object.create( null ); // створити "нульовий" об'єкт (такий самий, як і раніше)
Object.setPrototypeOf(ocn, Object.prototype); // встановити значенням прототипу нового об'єкта "загальний" об'єкт (НЕ стандартний об'єкт)
(На додачу до функцій, пов'язаних з рядками, що наведені вище, це також додає:)
> ocn.valueOf() // виводить {}
> ocn.hasOwnProperty("x") // виводить "false"
> ocn.constructor // виводить "Object() { [native code] }"
// ...та вся решта властивостей та методів Object.prototype.
Як бачимо, об'єкти, змінені таким чином, тепер виглядають дуже схожими на звичайні об'єкти.
Поліфіл
Цей поліфіл покриває основний сценарій використання, а саме, створення нового об'єкта, для якого був обраний прототип, але не бере до уваги другий аргумент.
Зауважте, що в той час як використання null
в якості [[Prototype]]
підтримується в реальному методі ES5 Object.create
, цей поліфіл не може це підтримувати через обмеження, притаманне версіям ECMAScript нижче 5.
if (typeof Object.create !== "function") {
Object.create = function (proto, propertiesObject) {
if (typeof proto !== 'object' && typeof proto !== 'function') {
throw new TypeError("Прототипом об'єкта може бути тільки об'єкт: " + proto);
} else if (proto === null) {
throw new Error("Реалізація Object.create у цьому браузері є шимом та не підтримує 'null' в якості першого аргументу.");
}
if (typeof propertiesObject != 'undefined') {
throw new Error("Реалізація Object.create у цьому браузері є шимом та не підтримує другий аргумент.");
}
function F() {}
F.prototype = proto;
return new F();
};
}
Специфікації
Специфікація | Статус | Коментар |
---|---|---|
ECMAScript 5.1 (ECMA-262) The definition of 'Object.create' in that specification. |
Standard | Початкове визначення. Реалізоване у JavaScript 1.8.5. |
ECMAScript 2015 (6th Edition, ECMA-262) The definition of 'Object.create' in that specification. |
Standard | |
ECMAScript (ECMA-262) The definition of 'Object.create' in that specification. |
Living Standard |
Сумісність з веб-переглядачами
BCD tables only load in the browser