Enclosing Scopes
Forgetting about this
for a moment. The concept of "execution context" is called Closure in Javascript. The MDN article explains it well.
To get a better sense of it, let's use a variable called a
instead of this
:
const a = 1;
function foo() {
//can access `a` here
function bar() {
//can also access `a` here
}
}
These functions have access to the outer scopes where they were declared. This is the "execution context", as you called it, though this is the wrong terminology for Javascript. "Scope" or "lexical scope" would be the correct terms.
No matter where you call those functions, they will still be able to print a
.
Call Stack and Heap
The MDN article on Closures has this to say:
Closure Scope Chain
Every closure has three scopes:
- Local Scope (Own scope)
- Outer Functions Scope
- Global Scope
The thing to note about these is that they are established at parse time, NOT execution time.
While Javascript does have a call stack and a heap, these have nothing to do with scope or closures. Scopes are a property of the function, and remain the same from the time the Javascript is first parsed until the code has finished running.
About this
this
is a variable that depends entirely on how the function was initialized and called. The MDN article may again prove useful here.
Let's look again at your example function, with some inline comments and examples:
function foo() {
console.log(this);
}
// Sure, calling `foo()` right now will print `window` if you're in a browser
foo(); //prints `window` in a browser
// But any use of `new` keyword will change `this` (including classes)
const obj = new Object();
obj.foo = foo;
obj.foo(); //prints `{ foo: [Function: foo] }`
// Similarly, assigning `foo` to a plain object will also change `this`
const obj = {};
obj.foo = foo;
obj.foo(); //prints `{ foo: [Function: foo] }`
// Lastly, any of the `Function.prototype` methods `bind`, `apply`, `call`
// intentionally change `this`
//
// `bind()` returns a new function:
const boundFunction = foo.bind({"this is a property": "and a value, see!?"});
boundFunction(); //prints `{ 'this is a property': 'and a value, see!?' }`
//
//`apply()` and `call()` both execute the function
// (the difference between the two is in how arguments are passed,
// see the MDN articles on
// [apply](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/apply#syntax) and on
// [call](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/call#syntax) for more details)
foo.apply({bar:1}); //prints `{ bar: 1 }`
foo.call({baz: 2}); //prints `{ baz: 2 }`