"Get the fundamentals down and the level of everything you do will rise" - Michael Jordan
Today I'm starting something new on this blog. This year, one of my goals has been to master javascript fundamentals. I've created projects in the past using Javascript, Objective-C, Swift, and Ruby - but because my goal was always to build something, I never built a rock solid foundation of knowledge. I would google and hack my way to solutions, but my speed and enjoyment suffered by the fact that I hadn't mastered the fundamentals.
In design, I have such an intuitive understanding of the principles, best practices, and tools - that I'm able to move quickly and happily from problem to solution. I want this fluid sense of mastery in code as well.
What I'm doing about it: Since December 2017, I've set the goal of 1 hour of learning Javascript everyday with the goal of mastering the fundamentals. Sure, I've missed days along the way as work requirements demand extra time, etc. - but I've done my best to stick to this regimen and it's paying off.
How I'm learning: 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. I've learned a lot of skills on the internet, and this is without a doubt one of the best programs I've found.
As I move forward with documenting my progress on this blog, I'll be keeping my notes on the blog to force me to clarify my understanding (to the point of being able to explain 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!
Anyway, that's what's going on here now - so I'll just jump in to today's notes:
Premium Membership Course
Chapter 5: AccountingJS
Lesson 4: IIFEs and sharing data
We use IIFEs so we can hide variables from the rest of the program, so we can avoid potential conflicts.
Example:
(function() { // sandwich.js: A simple library for sandwich ingredients. // Demo usage: sandwichLibrary.breads.wheat ==> 'The healthy option' var breads = { wheat: 'The healthy option', white: 'The unhealthy option' }; var fillings = { turkey: 'For boring sandwiches', cheese: 'For the vegetarians' }; var sandwichLibrary = { breads: breads, fillings: fillings }; })();
The problem is, we can't access the variables inside of the IIFE. For example, if we try:
sandwichLibrary.breads.wheat
We get an error:
So how do we expose the sandwichLibrary to the rest of our program?
The window object.
Point 1: The window object actually represents the browser and has everything you need to program in the browser:
Point 2: Everything that's on the window is accessible throughout the program.
Every time we create a variable, it's actually saved to the window object - even though you might not realize it. It happens automatically.
So for example, if we create a variable called:
var variableThatIsNotInsideOfAFunction = 'not in a function';
That variable gets saved to the window object automatically. We can access it on the window object as shown here:
So this variable is now global.
And this this is the same thing as if we did this:
window.variableThatIsNotInsideOfAFunction = 'not in a function';
The only difference is that in the first case (where we just define the variable without referencing the window object explicitly), the variable gets added to the window object automatically.
You can change the variable's value:
window.variableThatIsNotInsideOfAFunction = 'still not in a function'; // is the same as variableThatIsNotInsideOfAFunction = 'still not in a function'; // so... window.variableThatIsNotInsideOfAFunction // is the same as... variableThatIsNotInsideOfAFunction
We're going to take advantage of this window object to expose our sandwichLibrary to the rest of our program, since it is now currently inside of an IIFE and can't be accessed.
Because the window object is accessible to everybody.
Variables created inside an IIFE are not saved to the window object. To do that we can access the window object explicitly from within the IIFE, in the same way we accessed the window object in the examples above.
(function() { // sandwich.js: A simple library for sandwich ingredients. // Demo usage: sandwichLibrary.breads.wheat ==> 'The healthy option' var breads = { wheat: 'The healthy option', white: 'The unhealthy option' }; var fillings = { turkey: 'For boring sandwiches', cheese: 'For the vegetarians' }; var sandwichLibrary = { breads: breads, fillings: fillings }; // access the document object explicitly from within the IIFE window.sandwichLibrary = sandwichLibrary })();
So we're creating a new variable on the window object, and we're setting it to the sandwich library variable that lives within our IIFE.
Once we've done that, the variable is accessible from the window object, as you can see here:
*Question: what if the values of sandwichLibrary change within the IIFE? Are those changes reflected in the document object as well? *
Continuing...
You can now also access the sandwichLibrary variable from other IIFEs:
We've now solved the problem that our code within the IIFE was hidden from the rest of our program. We fixed it by explicitly saving the object we wanted to expose (sandwichLibrary) to the window object, making it globally accessible.
Now let's take a step back.
If the whole point was to hide the variable from the rest of our program by placing it within an IIFE, what's the point of this whole exercise if we're just going to go and make it globally accessible?
The reason is control.
A library may have a lot of variable names, and with a lot of variable names comes a potential for naming conflicts with the rest of the program.
With the approach described here - explicitly saving a variable from within the IIFE to the window object - we do create the potential for naming conflict on that one variable (in this case, sandwichLibrary), but we prevent the potential for conflict on all the other variable names. In this case, "breads" and "fillings".
So in creating the library we can do whatever we want creating variables within the IIFE, and then just expose one variable name ("sandwichLibrary") through which the rest of the data is accessible.
So if we look at the accounting.js library, we can see that the same this is happening.
And if you look at the function parameters, the first argument is 'root'.
So when you call this function with 'this' as the argument for 'root', 'root' will be set to 'this', which is the window object.
At the bottom of the library we can see:
root['accounting'] // is the same as window.accounting
And they're setting window.accounting to lib, which is the library object. This is the step where the library object is "exported and referenced globally" - mentioned in the comment above the variable definition at the top of the IIFE:
This is the same pattern used in the sandwich library, just with a slightly different approach by passing in this as root.
We can replicate that on sandwichLibrary like this:
(function(root, undefined) { // sandwich.js: A simple library for sandwich ingredients. // Demo usage: sandwichLibrary.breads.wheat ==> 'The healthy option' var breads = { wheat: 'The healthy option', white: 'The unhealthy option' }; var fillings = { turkey: 'For boring sandwiches', cheese: 'For the vegetarians' }; var sandwichLibrary = { breads: breads, fillings: fillings }; // window.sandwichLibrary = sandwichLibrary; root.sandwichLibrary = sandwichLibrary; })(this);
Because this is equal to the window object, and we are passing this as the value for the root argument. So within the function, we can set root.sandwichLibrary to sandwichLibrary, and it's the same thing as when we had window.sandwichLibrary = sandwichLibrary.
And to confirm, the intended usage of the library still works, allowing us to call:
sandwichLibrary.breads.wheat
One last thing:
The syntax is still slightly different from the accounting.js example.
So far, we've used dot notation to access properties, but accounting.js uses bracket notation, which we can also do.
It's all the same thing, just different notation.
So to summarize, today was about learning how to expose variables from within an IIFE to that it can be used by the rest of the program. Put more concretely:
Problem: IIFEs are a good way to hide variables from the rest of the program (useful when building libraries) because they avoid naming conflicts. But then we're unable to access the variables from outside of the IIFE/library.
Solution: We can exploit the global nature of the window object. From within the IIFE, we save the variable we want to expose to the document object. Then, that variable can be accessed globally via the window object.
Examples:
// dot notation window.sandwichLibrary = sandwichLibrary; // bracket notation root['sandwichLibrary'] = sandwichLibrary;
That's it for today!