Get the fundamentals down and the level of everything you do will rise. - Michael Jordan
Continuing on day 4 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!
This is part 2 about librarySystem. For part 1, please click here.
LibrarySystem Part 2
Here is a side by side comparison of the two approaches:
In the new system, on line 20, we say "hey librarySystem, go get me the sandwichLibrary".
Arguably the approach on the left is a little bit nicer when using the library, but you need to get used to both.
For example in NodeJS, you'll use require, which is a lot like the right side approach.
So lets say you're the library author - you have this real problem that developers might use the left side approach, or the right side approach - you don't know.
One solution: I'll just have two versions (the left and the right). But this isn't good because you'd have to make the same changes in each every time you update.
The best solution would be to just have one file, and it just dynamically does the right thing depending on what's available.
For example, you could use logic such as: if the librarySystem is defined, use the approach on the right side, if not - use the approach on the left.
So how would you do something like that?
Well, let's naively try to do both:
(function() { 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 }; // Handle window case. window.sandwichLibrary = sandwichLibrary; // Handle librarySystem case. librarySystem('sandwichLibrary', function() { return sandwichLibrary; }); })();
This is a good start, but it's kind of dumb. It's not dynamically choosing the right thing.
A smarter way is to put these in an if/else statement to choose between the two cases. Something like this:
(function() { 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 }; // Handle window case. window.sandwichLibrary = sandwichLibrary; if (/* librarySystem function exists */) { // Handle librarySystem case. librarySystem('sandwichLibrary', function() { return sandwichLibrary; }); } else { // Handle window case. window.sandwichLibrary = sandwichLibrary; } })();
The last thing we need to do is get the if condition to work.
Specifically, we need to know if librarySystem is undefined, because then we know we're in an environment where the developer is not using a librarySystem.
The way we do this in javascript is a little clunky.
We need to get the type of the librarySystem variable.
Let's go to the console and see what that means.
There's a keyword typeof that gives you the type of whatever you give it.
So we want to use this check to see whether or not the librarySystem function is undefined.
So on a new page, this is undefined.
But if we define it:
Then we check the type, we see that it's a function:
So we want to see: librarySystem defined? If it is, we'll use that method. If it isn't defined, then we won't use it.
Using what we know now, let's add the logic to the if statement:
(function() { 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 }; // Handle window case. window.sandwichLibrary = sandwichLibrary; // if librarySystem is undefined if (typeof librarySystem !== 'undefined') { // Handle librarySystem case. librarySystem('sandwichLibrary', function() { return sandwichLibrary; }); } else { // Handle window case. window.sandwichLibrary = sandwichLibrary; } })();
And that's pretty much it.
It's the same library code as before. We have our breads, fillings, and we're creating our sandwichLibrary object.
If librarySystem exists, then we:
- Call librarySystem
- Give librarySystem the name of the library ('sandwichLibrary').
- Give it a function that returns the 'sandwichLibrary' library object.
If librarySystem doesn't exist, then we:
- Attach the sandwichLibrary object to the window, and create a global variable.
Now your library will dynamically do the right thing.
This is the approach that accountingJS takes, and pretty much every other open source javascript library.
Let's use what we've made so far by putting this code in the console and putting a debugger statement in above our if/else check to see if librarySystem is undefined.
Since we've just refreshed the page, librarySystem doesn't exist, and we skip over the if case, and go directly to line 26:
This line attaches sandwichLibrary to the window object.
There's no return value:
We can now access sandwichLibrary globally:
In this case we went into the else statement because librarySystem didn't exist. But what if it does exist?
Let's paste in our new code with the if/else statement that checks to see if librarySystem is undefined, and add a debugger statement again:
This time, librarySystem does exist.
When we go into librarySystem, we see that libraryName is 'sandwichLibrary':
Since we have more than one argument, we go into the "create" section of the code:
Here we create a new property on the libraryStorage object, called 'sandwichLibrary', and it's going to be equal to the return value of our callback function.
Remember that all our callback function does is return the sandwichLibrary.
By the time the librarySystem function has finished running, we can see that the libraryStorage object now contains the sandwichLibrary object.
That's in for Part 2. In Part 3, we'll look at how Accounting JS handles this.
Summary
- When building a library, we don’t know if the developer prefers to create a global variable on the window object for every library, or use librarySystem.
- To solve this, we can create an if/else statement that checks to see if librarySystem exists.
- If librarySystem exists, we call the librarySystem function.
- If librarySystem doesn’t exist, we attach sandwichLibrary directly to the window object.