1

There are many ways to create objects. However, I wanted to know why I am getting an error with this approach (just attempting to learn) - What is meant by 'undefined' in the error message? The problem is not related to how the object was first instantiated as I have tried var Obj={}; and obtained same result. Thx.

'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());
8
  • 1
    It means that Obj.prototype is undefined. Commented Oct 27, 2017 at 20:32
  • 2
    Possible duplicate of Creating Js object with Object.create(null)? Commented Oct 27, 2017 at 20:32
  • @barmar, thank you. Why is it not defined? Isn't the prototype implicitly created? Commented Oct 27, 2017 at 20:34
  • 3
    @NoChance By creating an object with Object.create() the argument you pass in IS the object's prototype. The options for this are either an Object or null. You have passed in null, which it treats as undefined in this case. Commented Oct 27, 2017 at 20:38
  • 2
    @NoChance Because only function objects are created by default with a .prototype property Commented Oct 27, 2017 at 21:57

2 Answers 2

3

So, basically, I think this boils down to a fundamental misunderstanding of what a prototype is. Individual instances of an object do not have a .prototype, rather, they have an internal link to the prototype of the constructor from which the object instance was created. This was formerly known as .__proto__ (AKA dunder-proto), but has since been officially deprecated.

More recently, to reference the prototype of the constructor for an object instance, you can access a property called .constructor. (*Note*: .constructor may be undefined depending on how the object was created). From this, you can access the .prototype.

Similarly, you can use Object.getPrototypeOf(obj) and Object.setPrototypeOf(obj) where obj is an instance of an object.

For example:

var x = Object.create(null);
console.log("x.prototype", x.prototype);

var y = Object.create({a: "foo"});
console.log("y.prototype:", y.prototype);

The .prototype is undefined in both cases, because object instances do not have a prototype property, only object constructors do.

That being said, we can access the prototype that an object instance was created from by using the .constructor.prototype property, like so:

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);

Sign up to request clarification or add additional context in comments.

8 Comments

Thank you for your through explanation of the problem. One day, I may get how this language works.
Please weed out any mentions of the deprecated __proto__ getter/setter. No, objects don't have a __proto__ themselves, they have an internal prototype chain link - usually called [[prototype]]. It should always be accessed using Object.getPrototype.
@Bergi True, I think it's a matter of semantics, which is why I didn't say __proto__ property. You're right that __proto__ is simply an internal prototype chain link. Your point about it being deprecated is valid, I just used that as an example to illustrate the difference. Saying getPrototype when .prototype is undefined can be pretty confusing. However, for the sake of future readers, I will update my post with your suggestions.
@mhodges y does have a prototype. But, it's a property of its constructor. Try this: console.log(y.constructor.prototype===Object.prototype);
@slevy1 I would disagree. y points to a prototype, but does not own one itself. For this reason, you cannot change an object instance's prototype without changing the prototype for the object constructor - it can only be referenced via its constructor.
|
3

The OP's interesting example merits close inspection to best grasp the resulting error as well as how to improve the code.

Passing null to Object.create() produces an undefined object which causes the error to occur. The correction involves passing instead a prototype object, such as Object. The result: Obj possesses a valid and defined object with all the methods of the Object prototype.

Variable descriptor holds a valid object whose prototype incidentally is accessible as descriptor.constructor.prototype. More information about constructors here.

The statement adding a property to Obj is correct, but of limited usefulness. It adds a property directly to the Obj and not to its prototype. For that reason, adding the method f to the prototype is awkward since it can only access the property p0 of the specific object Obj owing to the property not belonging to the prototype of Obj. The final statement indicates that method f() works correctly with respect to Obj when the code below executes:

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

The OP may wish to revise the code as follows:

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

The advantage of this second example is that all objects instantiated from the Obj prototype will have an f method and a p0 property.

Note: in re the 'use strict' statement, one should be aware that not all browsers support it; see here.

7 Comments

Thank you. Obj.p0=decriptor is indeed new to me!
Not exactly, f should be on Obj's prototype, not on Obj itself. Obj.hasOwnProperty("f") should be false
@mhodges, you are correct again. The code will only work for one object. You may want to put your comment in an answer.
This changes Obj from a plain object into a constructor function. Not sure whether the OP wanted that
Thank you everyone for your comments; have revised the code accordingly so this should be a big improvement.
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.