Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How does "this" work in functions that are assigned in the constructor?

I found this example code:

function personFullName() {
    return this.first + ' ' + this.last;
}

function Person(first, last) {
    this.first = first;
    this.last = last;
    this.fullName = personFullName;
}

var dude = new Person("Michael", "Jackson");
alert(dude.fullName());

Which alerts "Michael Jackson". I changed it to call personFullName from the constructor instead of assigning the function object:

function personFullName() {
    return this.first + ' ' + this.last;
}

function Person(first, last) {
    this.first = first;
    this.last = last;
    this.fullName = personFullName();
}

var dude = new Person("Michael", "Jackson");
alert(dude.fullName);

I would expect the "fullName" property to now be a string instead of a function. But now it alerts "undefined undefined". Can anyone explain why my version doesn't work?

like image 808
random_stuff Avatar asked May 19 '15 15:05

random_stuff


2 Answers

In JavaScript, this is typically whatever comes before the . in the function call. So the fact that you said dude.fullName() is what caused this in fullName() to be set to dude1.

In the second version in your question, you're not calling it the same way. You're calling personFullName() without anything in front of it (which is correct, since it's no longer attached to a Person object). That means that this ends up defaulting to the same value as window. Since window has no first or last properties set on it, this.first and this.last are undefined.

To fix this, you can make your person be an argument to the personFullName() function:

function personFullName(person) {
    return person.first + ' ' + person.last;
}

and then call it like

…
this.fullName = personFullName(this);

1: Note that the method has to be a property on the object for the this binding to work. You can't just call object.someMethod() and get have this set to object in someMethod. In your code, the following would not work:

function Person(first, last) {
    this.first = first;
    this.last = last;
    this.fullName = this.personFullName();
}

Uncaught TypeError: this.personFullName is not a function

Neither would this:

function personFullName() {
    return this.first + ' ' + this.last;
}

function Person(first, last) {
    this.first = first;
    this.last = last;
}

var dude = new Person("Michael", "Jackson");
alert(dude.personFullName());

Uncaught TypeError: dude.personFullName is not a function

You can get around this restriction in any situation with the apply helper method: this.fullName = personFullName.apply(this) does what you expect the second version of your code to do and you can also call personFullName.apply(dude) at any point and get "Michael Jackson" back.

like image 158
StriplingWarrior Avatar answered Oct 16 '22 15:10

StriplingWarrior


this is the window in your personFullName function as it wasn't called in the correct context. You can use apply to call it with the correct context without modifying the personFullName function.

function personFullName() {
    return this.first + ' ' + this.last;
}

function Person(first, last) {
    this.first = first;
    this.last = last;
    this.fullName = personFullName.apply(this); // The magic
}

var dude = new Person("Michael", "Jackson");
alert(dude.fullName);
like image 43
Matt Derrick Avatar answered Oct 16 '22 17:10

Matt Derrick