It's confusing to me to read through this and understand the nature of variable declaration and reference in JavaScript with the emphasis on demonstrating scenarios rather than describing the basic principles. I understand though that some people would find this approach more useful than my personally prefered approach. So don't take the rest of my post as criticism, but addendum for folks who think differently.

I think "this" in JavaScript becomes much more clear (though only subjectively) when you start playing with bind/call/apply.

There are three, separate contexts in JS. There is the declared function block (Basic lexical scope, and other types of blocks don't create their own scope), there is "this" context, and then there is the much less understood context "with" which you're working (a sort of dynamic scope that is determined lexically).

I call them "contexts" instead of "scopes", because "this" isn't a scope level, and it is not contained in any scope level. It's a keyword that references an object value that is determined by a different set of rules than scope. If it were "in scope", then we could arrange scenarios in which we could chain references to "this". There is no such thing as window.this, or window["this"].

All variables (and named functions are function objects bound to a variable name) are stored within an object somewhere. When you declare a variable, how you declare it specifically sets the context object in which you're storing the variable. "var x = 5", you're setting the variable on the function block context. "this.x = 5", you're obviously setting on the "this" context. "x = 5" declares the variable in the "with" context, if it's not already declared in the function block. In all cases, it's first, "figure out some obj on which to store this variable" followed by "set obj[fieldName] to value", where fieldName is a string (always a string).

Accessing the variable again is the same sort of process. "Figure out what containing object we're talking about, then access it as obj[fieldName]". In all cases. The complexity lies in the figuring out step.

Now that we know that, we can start to ask questions about how these three scopes are set.

Function block scope is always function block scope. Unless you create a new function block (and only function blocks get their own scope level), then bare variable references first check the function block scope. Also remember that variable declarations get "hoisted" to the top of the function. If you declare a variable in a function, it's visible in the entire block. But, the variable is not set until the line on which it was originally declared.

function(){
console.log(x); // prints "undefined"
//console.log(z); would error, as no z is ever declared
var x = "foo";
console.log(x); // prints "foo"
}

"with" scope is determined at the call site, by walking up the call lexical scope until a declaration for the requested variable is found. If it gets all the way up to the top "window" and it's still not found, you get an "undeclared variable" error.

When you reference a function, what it will use as "this" then depends on whether or not it was explicitly set with bind/call/apply, followed by where it was found by the scope rules. If this wasn't set explicitly, then the object in which the variable was stored is used as "this".

So that means, with respect to event handlers, that the handler functions must be getting stored as a property of the DOM element to which they are bound.

But you can always override "this" with bind/call/apply. Just be careful that you know what "this" means when you call "func.bind(this)".

It's not accurate, because the syntax suggests there is a higher context to check, but if it helps you, you could think of the global script level in a browser as:

function window(){
with(window){
// all the code in your script block
}
}
window.call(window);

Every "var" or bare or "this" declarations are going to get set on the window object, every bare or "this" references are going to be from the window object.

From there, a bare function call changes the function block scope, but not "this" or "with". Calling with "new" changes function block and "this", but not "with". And creating a with block changes changes "with", but not "this" or function block. Calling "bind" sets "this" explicitly, and prevents it from automatically changing--though it can be reset explicitly with another call to bind. There is no way for one function block scope to have "this" change.