Существует много способов создания объектов. Тем не менее, я хотел знать, почему я получаю сообщение об ошибке с этим подходом (просто пытаюсь узнать). Что означает "undefined" в сообщении об ошибке? Проблема не связана с тем, как объект был впервые создан, поскольку я пробовал var Obj = {}; и получил тот же результат. Спасибо.
'use strict';
var Obj=Object.create(null);
var descriptor={
value: 37, writable: true, enumerable: true, configurable: true};
Object.defineProperty(Obj, 'p0' , descriptor);
// Next line causes an error: Cannot set property 'f' of undefined
Obj.prototype.f = function() { //define a method f() on Obj PROTOTYPE
return ('hello ${this.p0}');
};
console.log(Obj.f());
Поэтому, в основном, я думаю, что это сводится к фундаментальному непониманию того, что такое prototype
. Отдельные экземпляры объекта не имеют .prototype
, скорее, они имеют внутреннюю ссылку на прототип конструктора, из которого был создан экземпляр объекта. Ранее это было известно как .__proto__
(AKA dunder-proto), но с тех пор оно официально устарело.
Совсем недавно, чтобы ссылаться на прототип конструктора для экземпляра объекта, вы можете получить доступ к свойству, называемому .constructor
. (* Примечание *: .constructor
может быть неопределенным в зависимости от того, как был создан объект). Из этого вы можете получить доступ к .prototype
.
Аналогичным образом вы можете использовать Object.getPrototypeOf(obj)
и Object.setPrototypeOf(obj)
где obj
- это экземпляр объекта.
Например:
var x = Object.create(null);
console.log("x.prototype", x.prototype);
var y = Object.create({a: "foo"});
console.log("y.prototype:", y.prototype);
.prototype
не определен в обоих случаях, потому что экземпляры объектов не имеют свойства прототипа, только конструкторы объектов.
При этом мы можем получить доступ к прототипу, из которого был создан экземпляр объекта, используя свойство .constructor.prototype
, например:
function myObj (){
this.a = "foo";
}
// myObj is a constructor function, which has a prototype that we can set properties on
myObj.prototype.b = "bar";
// obj is an instance of an object, which does not have a prototype property
var obj = new myObj();
console.log("obj.prototype:", obj.prototype);
console.log("Object.getPrototypeOf(obj):", Object.getPrototypeOf(obj));
console.log("obj.constructor.prototype:", obj.constructor.prototype);
__proto__
любые упоминания об устаревшем __proto__
getter / setter. Нет, объекты сами по себе не имеют __proto__
, у них есть внутренняя цепочка прототипов - обычно называемая [[prototype]]. К нему всегда нужно обращаться, используя Object.getPrototype
.
Интересный пример OP заслуживает тщательной проверки, чтобы лучше понять полученную ошибку, а также как улучшить код.
Передача null в Object.create() создает неопределенный объект, который вызывает ошибку. Коррекция включает в себя передачу вместо объекта прототипа, такого как Object. Результат: Obj имеет действительный и определенный объект со всеми методами прототипа Object.
Переменный дескриптор содержит действительный объект, прототип которого кстати доступен как descriptor.constructor.prototype
. Подробнее о конструкторах здесь.
Утверждение, добавляющее свойство Obj, является правильным, но имеет ограниченную полезность. Он добавляет свойство непосредственно к Obj, а не к его прототипу. По этой причине добавление метода f к прототипу неудобно, поскольку он может получить доступ только к свойству p0 конкретного объекта Obj из-за свойства, не принадлежащего прототипу Obj. Заключительный оператор указывает, что метод f() работает правильно относительно Obj, когда выполняется следующий код:
'use strict';
// creates undefined object
var Obj=Object.create(null);
console.log("Obj is " + Obj.constructor);
// instantiate Obj of type Object
Obj = Object.create( Object );
console.log("Obj is now instance of Object? " + (Obj instanceof Object));
var descriptor={
value: 37, writable: true, enumerable: true, configurable: true};
// directly add property to object Obj
Object.defineProperty(Obj, 'p0', descriptor);
// adding new method to Obj prototype
Obj.prototype.f = function() {
return ("hello " + Obj.p0);
};
console.log(Obj.f());
ОП может пожелать пересмотреть код следующим образом:
'use strict';
var descriptor =
{
value: 37,
writable: true,
enumerable: true,
configurable: true
};
/* instantiate Obj of type Object
* like this:
* var Obj = Object.create( Object );
* Or:
*/
var Obj = Object.constructor;
// make p0 a property of the Obj prototype
Obj.prototype.p0 = descriptor;
// add method to Obj prototype
Obj.prototype.f = function() {
return ("hello " + this.p0.value);
};
console.log( Obj.f() );
Преимущество этого второго примера состоит в том, что все объекты, созданные из прототипа Obj, будут иметь метод f и свойство p0.
Обратите внимание: в re 'use strict', следует помнить, что не все браузеры поддерживают его; см. здесь.
f
должен быть на прототипе Obj, а не на самом Obj. Obj.hasOwnProperty("f")
должен быть ложным
Obj.prototype
не определен.