Reputation: 9701
I'm learning JavaScript (using latest Google Chrome build on 64bit Debian Jessie) for a couple of days now and right now I'm stuck in understanding how exactly Object.defineProperty
works. I have the following page:
function createPerson() {
var person = {
firstName: 'Lilly',
lastName: 'Louis',
};
Object.defineProperty(person, 'fullName', {
get: function() {
return this.firstName + ' ' + this.lastName;
},
set: function(name) {
var words = name.split(' ');
this.firstName = words[0] || '';
this.lastName = words[1] || '';
}
});
}
var p1 = createPerson();
console.log("Person's default name is \"" + p1.fullName + "\"");
I get Uncaught TypeError: Cannot read property 'fullName' of undefined
at the line where I try to use the property inside console.log(...)
. If I place both the definition of person
along with the Object.defineProperty
outside the function everything works fine. If I move only Object.defineProperty
outside the function after p1
is created like this:
var p1 = createPerson();
Object.defineProperty(p1, 'fullName', {
get: function() {
return this.firstName + ' ' + this.lastName;
},
set: function(name) {
var names = name.split(' ');
this.firstName = names[0] || '';
this.lastName = names[1] || '';
}
});
I get Uncaught TypeError: Object.defineProperty called on non-object
followed by the console pointing out that it is invoked on an (anonymous function)
(that is p1
) which is even more confusing since I've just read that if I assign callPerson
to my p1
without the brackets I'm passing the function object to it otherwise I'm calling the function itself and assigning its return value (if none, undefined is returned) to p1
.
I have the feeling both issues are completely separate but still since I'm not 100% sure I've decided to put both in the same question. Any advice what I'm doing wrong here? I can define my getter
and setter
the old fashioned way by simply doing
function createPerson() {
var person = {
firstName: 'Lilly',
lastName: 'Louis',
getFullName: function() {
return ...;
}
setFullName: function(name) {
...
}
};
}
but I would like to understand what's going on here. :D
Upvotes: 1
Views: 1136
Reputation: 21575
This is because you created a person
variable, but never returned it. Hence nothing is being returned from the function, and the assignment is undefined:
function createPerson() {
var person = {
firstName: 'Lilly',
lastName: 'Louis',
};
Object.defineProperty(person, 'fullName', {
get: function() { return this.firstName + ' ' + this.lastName; },
set: function(name) {
var words = name.split(' ');
this.firstName = words[0] || '';
this.lastName = words[1] || '';
}
});
return person; // Add this line
}
Upvotes: 1
Reputation: 1074585
Your defineProperty
call is absolutely fine. The problem is that createPerson
never returns anything, so the result of calling it is undefined
. You want return person;
at the end:
function createPerson() {
var person = {
firstName: 'Lilly',
lastName: 'Louis',
};
Object.defineProperty(person, 'fullName', {
get: function() {
return this.firstName + ' ' + this.lastName;
},
set: function(name) {
var words = name.split(' ');
this.firstName = words[0] || '';
this.lastName = words[1] || '';
}
});
return person; // <=======
}
var p1 = createPerson();
console.log("Person's default name is \"" + p1.fullName + "\"");
Alternately, make it a constructor function (used with new
) rather than a factory function, and use this
within the function. In that case, we'd drop create
from the name, and we wouldn't (typically) use return this;
at the end because if the function doesn't return a different object, by default the result of new XYZ
is the object created by the new
operator and supplied to the function as this
:
function Person() {
this.firstName = 'Lilly';
this.lastName = 'Louis';
Object.defineProperty(this, 'fullName', {
get: function() {
return this.firstName + ' ' + this.lastName;
},
set: function(name) {
var words = name.split(' ');
this.firstName = words[0] || '';
this.lastName = words[1] || '';
}
});
}
var p1 = new Person();
console.log("Person's default name is \"" + p1.fullName + "\"");
Upvotes: 5