Best way I can think to describe `this`:
-----
1. Either you decide in advance exactly what you want `this` to be:
- Use `.bind()` on your function and fix the value of `this`
- Create an arrow function and get `this` from the lexical scope
- Alias `let self = this` in your desired scope, and only ever reference `self`
- Create a wrapper function which fixes the value of `this` (essentially, implement `.bind()`)
-----
2. Or `this` will be decided by your caller:
- Your caller will `.bind()` your function with an unknown value of `this`
- Your caller will call `.call()` or `.apply()` with an unknown value of `this`
- Your caller will attach your function to an unknown object, then call `unknownObject.yourFunction()`, setting `this` to `unknownObject`
- Your caller will call your function as a standalone function: `let newFunc = someObj.someFunc` - then call it, setting `this` to `global` or `window`
- Your caller will pass your function as a param to something like `setTimeout` and achieve the same effect, setting `this` to `global` or `window`
-----
Obviously, most of the time if you're going to use `this`, #1 is to be preferred, #2 is to be avoided, so it's imperative to decide in advance what you want `this` to be rather than allowing it to be decided by your caller.
(Unless that's absolutely what you want, and you have a good reason why -- like if you're writing a wrapper function which is agnostic to the value of `this` that's passed)