TypeError: Cannot set property '0' of undefined

In my constructor I define my variable like this:

constructor(){
   this.myvar = [];
}

and then, inside a function, I init the array like this:

initSutff(){

     for (let i = 0; i < labels.length; i++) {
      this.myvar [i] = 1;
    }

and if I log the array I can see the values in the console. But if I try to access them inside a checkbox. changed function like this:

this.checkbox[y].changed(function () {
        console.log(this.labelDrawState);
        console.log(`checkbox ${x} is checked: ${this.checked()}`);

        if (this.checked() == true) {
          this.myvar [x] = 1;
        } else {
          this.myvar [x] = 0;
        }
 });

I get:

TypeError: Cannot set property ‘0’ of undefined

why can’t js see the variable?

Thanks!

Hi @xbix ,

The issue you are facing is caused by the concept of scope and context in JavaScript.

By default when outside of any functions, we are in the global context which means that this refers to the Window object (in a browser, here in a new tab) :

console.log(this); // -> Window about:blank

This is the same when you call it inside a function :

function hello() {
  console.log(this); // -> Window about:blank
}

But you can use call() or apply() to pass a context to a function as an object :

function hello() {
  console.log("Hello " + this.name);
}

hello.call({name: "Alice"}); // -> Hello Alice

Now in your case, you are creating an anonymous function (a function without a name) that is passed to the changed() method of the checkbox. But when calling this.myvar[x] = 1, this refers to the p5.Element itself, not the current instance of your class.

What you can do is use the Function.prototype.bind() method to bind the current object to the function you pass. It works like this :

let test;

class Test {
  
  constructor() {
    this.myvar = [];
    
    this.checkbox = createCheckbox('checkbox');
    
    this.checkbox.changed(function() {
      // This is now the current object
      console.log(this); // -> Test {myvar: Array[0], checkbox: , constructor: Object}
      console.log(this.checkbox.checked()); // -> get checkbox status
      this.myvar[0] = 5;
      
      console.log(this.myvar); // -> [5]
    }.bind(this));
  }
}

function setup() {
  test = new Test();
}

The method bind returns a new function with this set to the first argument, in our case the instance of the object. From that we can get the checkbox object.

Hope it helps :slight_smile:

2 Likes

Thank you Jospehh for the detailed and good explanation! Your solution seems to work, but now I get another error with the checkbox.checked() function:

this.checkbox.checked() is not a function

instead of bind, it’s a bit hacky but you can do something like this too, so that self becomes the “parent” object and still you have access to checkbox’s this:

let test;

class Test {
  
  constructor() {
    this.myvar = [];
    
    this.checkbox = createCheckbox('checkbox');

    const self = this;
    this.checkbox.changed(function() {
      console.log(this.checked()); // -> get checkbox status
      self.myvar[0] = 5;
    });
  }
}

function setup() {
  test = new Test();
}

(the code is not tested)

2 Likes

Yes this is because the code I posted is an example and in your code you used this.checkbox[y] so the checkboxes are stored in an array :wink:

1 Like

Thanks, Micuat for your contribution, I tested it and it works!

That makes sense, I’ll test it right away. Thank you very much!

1 Like