rbaleksandar
rbaleksandar

Reputation: 9701

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

Upvotes: 1

Views: 1136

Answers (2)

Spencer Wieczorek
Spencer Wieczorek

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

T.J. Crowder
T.J. Crowder

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

Related Questions