0

Can someone explain where my thinking goes wrong here ? I have a class that adds a mousedown event listener to a specified canvas. I make two instances of the class attached to two different canvases. I expect to get responses from both instances but all responses say they're from the second instance.

class Mycanvas{
  constructor (canvas,name){
    self = this;
    this.name=name;
    canvas.addEventListener('mousedown', mousedown);
  }
  function mousedown(){console.log(self.name)}
}
let mycanvas1 = Mycanvas(canvas1,'1');
let mycanvas2 = Mycanvas(canvas2,'2');

BUT, this code works fine :

class Mycanvas{
  constructor (canvas,name){
    this.name=name;
    canvas.addEventListener('mousedown', ()=>console.log(this.name));
  }
}
let mycanvas1 = Mycanvas(canvas1,'1');
let mycanvas2 = Mycanvas(canvas2,'2');
mkc
  • 1
  • 1
  • Because 1. `self` is a global variable (it's window.self) so you overwrite it every time a new Class is created. 2. Why use `self = this` in a Class (or anywhere else, really)? You absolutely don't need it. – Jeremy Thille Jan 20 '22 at 10:01
  • `this.self=this` would have also worked fine (albeit useless as @JeremyThille said) because then `self` isnt a global variable. – Jamiec Jan 20 '22 at 10:03
  • The solution is [How to access the correct `this` inside a callback](https://stackoverflow.com/q/20279484) (also relevant: [How does the "this" keyword work?](https://stackoverflow.com/q/3127429)) but the problem is *also* that you make an [implicit global](http://blog.niftysnippets.org/2008/03/horror-of-implicit-globals.html). Which is detailed in [What is the purpose of the var keyword and when should I use it (or omit it)?](https://stackoverflow.com/q/1470488) – VLAZ Jan 20 '22 at 10:06
  • @Jamiec how would `this.self=this` work? The problem is the value of `this` missing - how would you get the correct value of `this` through `this`? – VLAZ Jan 20 '22 at 10:07
  • @VLAZ if in that same example the method were changed to `function mousedown(){console.log(this.self.name)}`. It's pointless as I said but it would work as expected (unless im missing something which is entirely possible) – Jamiec Jan 20 '22 at 10:09
  • I had indeed also missed the lack of `new` – Jamiec Jan 20 '22 at 10:11
  • Damn and blast it. The code is paraphrased from more extensive code where "new" was used but "let" was omitted in "let self=this". Apologies for getting people to debug my mistakes. – mkc Jan 20 '22 at 10:53
  • I do think the "let self=this" is OK coding practice though. It is one of the three ways I have been told of to pass "this" to the listener function so that it can access the property of my object. – mkc Jan 20 '22 at 10:55
  • "*I do think the "let self=this" is OK coding practice though.*" but it cannot work if you declare the variable in one scope and try to use it in another where the variable is not present. [What is the scope of variables in JavaScript?](https://stackoverflow.com/q/500431) `let self` means it's visible in the constructor, not in other methods. – VLAZ Jan 20 '22 at 11:41
  • Apologies again - mousedown was meant to be in the constructor function scope – mkc Jan 20 '22 at 13:45

1 Answers1

0

You invoke Mycanvas(canvas1,'1') directly (without the new keyword), which, I believe, only calls its constructor without instanciating a new Class. This doesn't output the expected result and makes you believe you need the self=this hack. But classes work like this :

class Mycanvas{
  constructor (canvas, name){
    this.name=name;
    this.canvas = canvas;
  }
  logMyName() {
     this.canvas.addEventListener('mousedown', () => console.log(`My name is ${this.name}`));
  }
}

let mycanvas1 = new Mycanvas(canvas1,'1');
mycanvas1.logMyName();

let mycanvas2 = new Mycanvas(canvas2,'2');
mycanvas2.logMyName();
canvas {
    border: grey solid 1px;
    width: 50px;
    height: 50px;
    cursor: pointer;
}
<canvas id="canvas1"></canvas>
<canvas id="canvas2"></canvas>
Jeremy Thille
  • 26,047
  • 12
  • 43
  • 63
  • Many thanks for your answer Jeremy. Profuse apologies : the omission of new was a copy problem but the omission of let was a real problem. One day I might get used to javascript global variables , until then I'll stick a post-it note on my screen saying check local variables declarations FIRST. I think one of the self hack or arrow function or mousedown.bind(this) are needed. – mkc Jan 20 '22 at 11:34
  • ? No, why would you need a hack or even a `.bind(this)`? Run my example. When you click canvas1 it says '1' and when you click canvas2 it says '2'. No `this`, no `bind`, no hack. Just a clean class. – Jeremy Thille Jan 21 '22 at 08:34
  • My point was simply that as the mousedown function gets more complicated and the constructor and number of properties gets larger the arrow function becomes less ofan obvious choice. – mkc Jan 22 '22 at 10:44