Reputation: 19232
When using object literal notation to create a Javascript Object like so:
var objectLiteralCat = {
name: {first: 'frisky', last: 'LaBeouf'},
color: 'Red'
}
I can then alter an Attribute of the name
Property of objectLiteralCat
like so:
Object.defineProperty(objectLiteralCat,'name', {configurable: false});
Great, now no one else can delete the objectLiteralCat.name
Property or alter its enumerable Attribute.
I am experimenting with a "flavor" of prototypal pattern object creating and I would like to use Object.defineProperty to set the name Property Attribute configurable
= false
like above, here is the code:
var Cat = {
create: function(firstName, lastName, color){
var self = Object.create(this);
self.name.first = firstName;
self.name.last = lastName;
Object.defineProperty(self,'name', {configurable: false});
Object.freeze(self.name);
Object.freeze(self.color);
return self;
},
name: {first: '', last: ''},
color: ''
}
var cat = Cat.create('frisky','smith','white');
console.log(cat.name); //undefined
If, in the Cat.create
function I comment out the line:
Object.defineProperty(self,'name', {configurable: false});
Then console.log(cat.name)
outputs, as expected:
Object {first: "frisky", last: "smith"}
I can explicitly set the value
Attribute of the Cat.name
Property like so:
Object.defineProperty(self,'name', {value: self.name, configurable: false});
What I do not not understand is why doesn't the objectLiteralCat
call to Object.defineProperty
:
Object.defineProperty(objectLiteralCat,'name', {configurable: false});
set the the objectLiteralCat.name
to undefined
like:
Object.defineProperty(self,'name', {configurable: false});
does in my Cat.create
function?
Even if I comment out the Object.define
code in Cat.create
and move it outside after the cat
object is created like so:
var cat = Cat.create('fluffy', 'leboudf', 'white');
Object.defineProperty(cat,'name', {configurable: false});
console.log(cat.name);
The output of console.log(cat.name)
is again undefined
.
My objectLiteralCat
object:
Object {
name: [object Object]
color: Red
}
looks fundamentally the same to my "flavor of prototypal pattern object creation"
Object {
create: function(firstName, lastName, color){
var self = Object.create(this);
self.name.first = firstName;
self.name.last = lastName;
//Object.defineProperty(self,'name', {value: self.name, configurable: false});
Object.freeze(self.name);
Object.freeze(self.color);
return self;
}
name: [object Object]
color:
}
Looking at the console.log
of the two objects though, it looks like all the Properties of the object created with Cat.create
are nested in __proto__
I am sure that is a clue to the answer to my question but I am not advanced enough yet with Javascript to figure out the answer without help.
Upvotes: 3
Views: 91
Reputation: 371069
The difference is that, in your object literal, objectLiteralCat.name
refers to the name
object immediately on objectLiteralCat
. On the other hand, when calling Cat.create
, you make two name
s: one is on the prototype object (the object at Cat.name
), and the other is on the self
object. Object.defineProperty
does not look up the prototype chain to identify where/if the passed property name passed is on any of the prototype objects - rather, Object.defineProperty
defines the property immediately on the object passed to it, which in this case, is self
.
But, before
Object.defineProperty(self,'name', {configurable: false});
is called, references to self.name
- that is, in the lines
self.name.first = firstName;
self.name.last = lastName;
will refer to the closest object in the prototype chain with a name
property - which is the Cat.name
object. To illustrate, see how instantiating a cat mutates the Cat.name
prototype object:
var Cat = {
create: function(firstName, lastName, color){
var self = Object.create(this);
self.name.first = firstName;
self.name.last = lastName;
Object.defineProperty(self,'name', {configurable: false});
Object.freeze(self.name);
Object.freeze(self.color);
return self;
},
name: {first: '', last: ''},
color: ''
}
var cat = Cat.create('frisky','smith','white');
// Log the prototype object: it's been mutated!
console.log(Cat.name);
After that, when you call
Object.defineProperty(self,'name', {configurable: false});
this sets the name
property directly on self
, not on the prototype. But you haven't passed any value
to it, so it defaults to undefined
. The name
property now exists both on the prototype object (Cat.name.first
and Cat.name.last
), and on the instantiated cat
(=== undefined
).
Perhaps you want to explicitly create a new name
property directly on self
at the beginning, before assigning to self.name.first
:
var Cat = {
create: function(firstName, lastName, color){
var self = Object.create(this);
Object.defineProperty(self,'name', {configurable: false, value: {}});
self.name.first = firstName;
self.name.last = lastName;
Object.freeze(self.name);
Object.freeze(self.color);
return self;
},
name: {first: '', last: ''},
color: ''
}
var cat = Cat.create('frisky','smith','white');
console.log(cat.name);
Upvotes: 3
Reputation: 138477
While self
is a new instance inheriting the Cat
object, self.name
still references the global Cat.name
. Therefore if you do:
const obj = { prop: 1 }
Object.defineProperty(obj, "prop", { /*...*/ })
Its a different thing, as the property exists directly on the object itself and and is not inherited. If it gets inherited, e.g.:
const obj2 = Object.create(obj);
Object.defineProperty(obj2, "prop", {/*...*/})
then defineProperty
defines a new property on the object itself. It does not consider that it was set on the prototype already. Therefore you shadow it to undefined. To solve that you might wanna assign a new object for every instance, e.g.:
self.name = { first: firstName, last: lastName};
Upvotes: 2