I'm sure there are JS junkies who can talk in more depth, but here's the way I think about it.
When, for example, this.setChanges() is run, you want the "this" in the line:
this.hasChanges = true
... to refer to the Stimulus controller.
But if you do addEventListener(), the object that triggers the function becomes the "this" inside the function. So when window.addEventListener() is triggered, "this" refers to window. For this.element.addEventListener(), "this.element" is the "this." So when the event is triggered, this.element.addEventListener() ... runs the equivalent of:
this.element.hasChanges = true
... unless you bind it to the controller first.
But I do the same thing! I forget to bind and it's not working, I always just think, "Oh, I guess I need to bind."