rbaleksandar rbaleksandar - 5 months ago 16
Javascript Question

Object.defineProperty - scope and use inside function returning object?

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

Answer

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 + "\"");

Comments