Get the fundamentals down and the level of everything you do will rise. - Michael Jordan
Continuing on day 2 of posting my notes on this this blog as I learn to master Javascript fundamentals one day at a time.
As stated in my original post, I do 1 hour of video lessons from Watch and Code every day. If you're interested in learning Javascript in a way that goes beyond basic tutorials and gives you a foundational, practical knowledge without relying on frameworks - I'd highly recommend it. If you're reading these posts, please keep in mind that these are just my notes, and I'm not an expert (yet!). If your goal is also to master the fundamentals of Javascript, please head over to Watch and Code and start your journey there!
Here we go!
Imagine we have this IIFE:
(function() { var name = 'jacob'; function sayName1() { console.log(name); } // we use the window object to expose sayName1 window.sayName1 = sayName1; })();
Then we have another IIFE that's almost exactly the same, but we've changed sayName1 to sayName2:
(function() { var name = 'kenny'; function sayName2() { console.log(name); } // again, use the window object to expose sayName2 window.sayName2 = sayName2; })();
So now sayName1 and sayName2 are globally available and we can use those wherever we want.
As you'd expect, sayName1 prints out 'jacob', and sayName2 prints out 'kenny'.
Nothing too exciting here, but it will teach an important and subtle feature of Javascript.
This lesson is more of a vocabulary lesson than anything else, and this vocabulary is important to get right in order to move to the next level.
There is one sentence to remember:
Functions can always remember the variables that they could see at creation.
Looking at the example again:
The code in the body of the functions are exactly the same, but the result is totally different.
The behavior is dependent on the variables.
Looking at the sentence "Functions can always remember the variables that they could see at creation.", we know that sayName1 will always remember that name == 'jacob', and sayName2 will always remember that name == 'kenny'.
The variables that the functions could see at creation become a defining characteristic of those two functions, even thought the code in the functions is identical.
Let's look in the debugger to see what's going on. We're going to look at a new part of the debugger that will make it even more powerful for us: Scope.
Scope is a term in CS and programming to describe two things:
1. Where you are in the code.
2. What you can see from where you are.
So in this case, where we are is at line 1 - the debugger line. We're at the global scope, which is the Window object.
In the browser, the Global object is the Window.
So again, Scope is a word to describe:
1. Where you are: In this case Line 1.
2. What you can see from where you are: In this case we're not in a function, so we can only see the global Window object.
When you're in a function, you can look out, and you can obviously see variables declared within that function.
But when you're outside, you can't see in.
So in our example:
From here, when we're on line 1, we can see variables declared in sayName1(), like var name for example.
So let's continue to the next line in the debugger, and pay attention to the way the Scope section changes.
Once we step into sayName1(), everything changes.
Now Scope has 3 sections:
- Local
- Closure
- Global
Variables inside the function you're currently in are in the Local section.
They are local to the function we're in.
In our case there are no variables declared inside sayName1, so we don't see any in the Local section of the debugger. We only see the this keyword - an object that every function has access to. In this case this is Window.
There are only 3 cases where variables can be visible:
1. They're global (they're on the Window object).
2. They're local (you've defined them yourself inside of your function).
3. They're defined in other functions.
var name falls in to category number 3.
We can see it in the Closure section.
To illustrate further, let's make one change to our IIFE. Let's name it "enclosingFunction".
(function enclosingFunction() { var name = 'jacob'; function sayName1() { console.log(name); } window.sayName1 = sayName1; })();
If we run sayName1 in the debugger, we see we have more information. The debugger names the closure for us:
The whole point is to program with more control and to know exactly what is happening.
We can look at the debugger and know exactly where the variables are coming from.
People love to talk about the Closure feature of Javascript - it comes up in interviews all the time.
Next time someone asks, there are only two things you need to know:
It comes back to that statement:
"Functions can always remember the variables they could see at creation."
That's because functions save a reference to the variables they could see at creation. That's why sayName1 prints 'Jacob' and sayName2 prints 'Kenny'.
If you want explain further: obviously functions can see variables defined inside of them -- those are local variables.
But they can also see variables defined outside of them in enclosing functions. That's why the technical term for this concept is "Closure".
That's it.
Summary
- Functions can always remember the variables they could see at creation.
- They can see variables defined inside the function.
- They can see variables defined in enclosing functions.