''this' is undefined in JavaScript class methods

I'm new to JavaScript. New as far as all I've really done with it is tweaked existing code and wrote small bits of jQuery.

Now I'm attempting to write a "class" with attributes and methods, but I'm having trouble with the methods. My code:

function Request(destination, stay_open) {
    this.state = "ready";
    this.xhr = null;
    this.destination = destination;
    this.stay_open = stay_open;

    this.open = function(data) {
        this.xhr = $.ajax({
            url: destination,
            success: this.handle_response,
            error: this.handle_failure,
            timeout: 100000000,
            data: data,
            dataType: 'json',
        });
    };

    /* snip... */

}

Request.prototype.start = function() {
    if( this.stay_open == true ) {
        this.open({msg: 'listen'});
    } else {

    }
};
//all console.log's omitted

The problem is, in Request.prototype.start, this is undefined and thus the if statement evaluates to false. What am I doing wrong here?



Solution 1:[1]

I just wanted to point out that sometimes this error happens because a function has been used as a high order function (passed as an argument) and then the scope of this got lost. In such cases, I would recommend passing such function bound to this. E.g.

this.myFunction.bind(this);

Solution 2:[2]

JavaScript's OOP is a little funky (or a lot) and it takes some getting used to. This first thing you need to keep in mind is that there are no Classes and thinking in terms of classes can trip you up. And in order to use a method attached to a Constructor (the JavaScript equivalent of a Class definition) you need to instantiate your object. For example:

Ninja = function (name) {
    this.name = name;
};
aNinja = new Ninja('foxy');
aNinja.name; //-> 'foxy'

enemyNinja = new Ninja('boggis');
enemyNinja.name; //=> 'boggis'

Note that Ninja instances have the same properties but aNinja cannot access the properties of enemyNinja. (This part should be really easy/straightforward) Things get a bit different when you start adding stuff to the prototype:

Ninja.prototype.jump = function () {
   return this.name + ' jumped!';
};
Ninja.prototype.jump(); //-> Error.
aNinja.jump(); //-> 'foxy jumped!'
enemyNinja.jump(); //-> 'boggis jumped!'

Calling this directly will throw an error because this only points to the correct object (your "Class") when the Constructor is instantiated (otherwise it points to the global object, window in a browser)

Solution 3:[3]

None of the previous answers had the full solution for me, so posting mine here.

I had a class, which was returning an error when I ran forEach on the method reference.

e.g.

class Foo {
  hello (name) {
    return `hello ${name}`
  }

  doGreet (name) {
    return console.log(this.hello(name)) // <- 'this' is undefined
  }
}

// print some names...
const foo = new Foo();
(['nick', 'john']).forEach(foo.doGreet)

// TypeError: Cannot read property 'hello' of undefined
//     at doGreet (/.../test.js:7:17)

The solution was to the bind the context of the method's this within a constructor. i.e.

class Foo {
  constructor () {
    this.doGreet = this.doGreet.bind(this) // <- Add this
  }

  hello (name) {
    return `hello ${name}`
  }

  doGreet (name) {
    return console.log(this.hello(name))
  }
}

Solution 4:[4]

In ES2015 a.k.a ES6, class is a syntactic sugar for functions.

If you want to force to set a context for this you can use bind() method. As @chetan pointed, on invocation you can set the context as well! Check the example below:

class Form extends React.Component {
constructor() {
    super();
  }
  handleChange(e) {
    switch (e.target.id) {
      case 'owner':
        this.setState({owner: e.target.value});
        break;
      default:
    }
  }
  render() {
    return (
      <form onSubmit={this.handleNewCodeBlock}>
        <p>Owner:</p> <input onChange={this.handleChange.bind(this)} />
      </form>
    );
  }
}

Here we forced the context inside handleChange() to Form.

Solution 5:[5]

Use arrow function:

Request.prototype.start = () => {
    if( this.stay_open == true ) {
        this.open({msg: 'listen'});
    } else {

    }
};

Solution 6:[6]

This question has been answered, but maybe this might someone else coming here.

I also had an issue where this is undefined, when I was foolishly trying to destructure the methods of a class when initialising it:

import MyClass from "./myClass"

// 'this' is not defined here:
const { aMethod } = new MyClass()
aMethod() // error: 'this' is not defined

// So instead, init as you would normally:
const myClass = new MyClass()
myClass.aMethod() // OK

Solution 7:[7]

If a function has been used as a high order function (passed as an argument) the scope of this gets lost.

To fix this problem one can bind this to the function like @Eliux described:

this.myFunction.bind(this);

To automate this process like @DanielTonon wanted you can do this in the constructor:

Object.getOwnPropertyNames(YourClass.prototype).forEach((key) => {
  if (key !== 'constructor') {
    this[key] = this[key].bind(this);
  }
});

Solution 8:[8]

The bind() method creates a new function that, when called, has its this keyword set to the provided value, with a given sequence of arguments preceding any provided when the new function is called.

const module = {
  x: 42,
  getX: function() {
    return this.x;
  }
};

const unboundGetX = module.getX;
console.log(unboundGetX()); // The function gets invoked at the global scope
// expected output: undefined

const boundGetX = unboundGetX.bind(module);
console.log(boundGetX());
// expected output: 42

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 EliuX
Solution 2 xj9
Solution 3 Daniel Tonon
Solution 4 Nitin
Solution 5 Kasra
Solution 6 Oli
Solution 7 Walter Woshid
Solution 8 MD SHAYON