Get the fundamentals down and the level of everything you do will rise. - Michael Jordan
Continuing on day 3 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!
Let's get started.
librarySystem
Recall from the previous lessons that we were able to expose our library as a single variable on the window object like this:
(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 }; window.sandwichLibrary = sandwichLibrary; })();
But what if we had a program that used 100 libraries, now we've used up 100 global variable names.
This is ok in most cases, but we can do a little better.
Instead of having one global variable per library, we have one global variable in total - even if we have 1,000 libraries.
Let's outline the two steps we had to go to with the previous technique of attaching our libraries to the window object.
Step 1. Create: Run the code in an IIFE, and somewhere in that code, attach your library to the Window object.
Step 2: Use: When you want to use the library, since it's accessible globally, you can just grab it like this:
sandwichLibrary
Let's see our old approach and new approach side by side:
// Current approach. One global variable per library // 1. Create: Run library in IIFE, and attach to window. // 2. Use: Access library from window. // Another approach. One global variable period. // 1. Create: librarySystem('libraryName', function() { /* return library */}); // 2. Use: librarySystem('libraryName'); ==> returns library object
From a use perspective, they're pretty similar.
Implementation is slightly different.
Here we can handle step 1 of the approach:
(function() { var libraryStorage = {}; function librarySystem(libraryName, callback) { if (arguments.length > 1) { libraryStorage[libraryName] = callback() } } })();
So here we use a property of functions we already know, which is that functions can always remember the variables that they could see at creation.
That's what we're leveraging when we put var libraryStorage = {}; outside of the librarySystem function. Because if we left it in there, every time librarySystem runs, it would delete and recreate libraryStorage, and you'd lose everything. So by putting it outside, in the enclosing function, we still have access to the variable.
The 'if' statement is how we check that this is the 'create' step. When we create, we call librarySystem with two arguments: the 'libraryName' and the callback function that returns the library
By running this line of code (above) it will save this library (libraryName), in the libraryStorage object, at libraryName property. So this takes care of the "Create" step.
Next, we add the case for the "Use" step:
Now remember that the problem is that all of our code is in the is Immediately Invoked Function Expression, and we to expose the librarySystem function to the window, so we can access it elsewhere.
We do that like this:
Now, globally, we can access the librarySystem function and run code like Case 1 and Case 2:
So let's see how we would use this.
For step 1, remember we want to create the library. So we call librarySystem and pass in the library name and callback function. The callback function has to return the library object
Then when we want to use the sandwichLibrary, we do it like this:
This returns the actual sandwichLibrary.
Let's say you had an Immediately Invoked Function Expression, and this is just code for your app, and you want to use the sandwichLibrary, you can do this:
As you can see, we have access to the sandwichLibrary object.
One last thing with this code. Let's grab the same code and throw in a debugger statement.
Here, we can see the sandwichLibrary object in the libraryStorage properties:
That's it for today, I will continue this in a Part 2 tomorrow.
Summary
- Attaching libraries to the window object is a good way of reducing the number of global variables (and potential naming conflicts).
- But if you have 100 or 1,000+ libraries, you would still have a lot of global variables.
- There is another approach that allows us to expose a single global variable, such as "librarySystem", which is a function that lets us grab libraries by name, and create new ones.
Specifically:
1. Create: librarySystem('libraryName', function() { /* return library */});
2. Use: librarySystem('libraryName'); ==> returns library object