Get the fundamentals down and the level of everything you do will rise. - Michael Jordan
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!
All screenshots were annotated using Shotty.
noConflict
Remember from previous lessons that we had this if/else statement to check whether librarySystem existed:
(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 }; // if librarySystem is undefined if (typeof librarySystem !== 'undefined') { // Handle librarySystem case. librarySystem('sandwichLibrary', function() { return sandwichLibrary; }); } else { // Handle window case. window.sandwichLibrary = sandwichLibrary; } })();
There's a problem with the else case where it attaches sandwichLibrary to the Window object: what if sandwichLibrary already has a value before sandwichLibrary is loaded.
For example let's say window.sandwichLibrary is set to a string, and then sandwichJS runs and overwrites this string. This is a problem.
// window.sandwichJS has an original value. window.sandwichLibrary = 'Library with books about sandwiches'; // SandwichJS loads. (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 }; // if librarySystem is undefined if (typeof librarySystem !== 'undefined') { // Handle librarySystem case. librarySystem('sandwichLibrary', function() { return sandwichLibrary; }); } else { // Handle window case. window.sandwichLibrary = sandwichLibrary; } })();
So how do we use both when they are competing for the same name?
The open source community has come up with a solution for this. The solution goes something like this:
// window.sandwichJS has an original value. window.sandwichLibrary = 'Library with books about sandwiches'; // SandwichJS loads. (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 }; // if librarySystem is undefined if (typeof librarySystem !== 'undefined') { // Handle librarySystem case. librarySystem('sandwichLibrary', function() { return sandwichLibrary; }); } else { // Handle window case. window.sandwichLibrary = sandwichLibrary; } })(); // This will reset window.sandwichLibrary to the original value. // .noConflict will also return the sandwichLibrary object. var sanwichJS = sandwichLibrary.noConflict(); // You want to print window.sandwichLibrary (you want the string). console.log(sandwichLibrary); // We can still use sandwichJS. console.log(sandwichJS.breads.white);
So by calling .noConflict on sandwichLibrary, it resets the window.sandwichLibrary to the original value, and it also returns the sandwichLibrary object.
The code above won't actually work yet, because we haven't written the noConflict method, so let's do that in the else case, since that's the only place where it applies.
// window.sandwichJS has an original value. window.sandwichLibrary = 'Library with books about sandwiches'; // SandwichJS loads. (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 }; // if librarySystem is undefined if (typeof librarySystem !== 'undefined') { // Handle librarySystem case. librarySystem('sandwichLibrary', function() { return sandwichLibrary; }); } else { // Handle window case. var oldSandwichLibrary = window.sandwichLibrary; sandwichLibrary.noConflict = function() { window.sandwichLibrary = oldSandwichLibrary; return sandwichLibrary; }; window.sandwichLibrary = sandwichLibrary; } })(); // This will reset window.sandwichLibrary to the original value. // .noConflict will also return the sandwichLibrary object. var sandwichJS = sandwichLibrary.noConflict(); // You want to print window.sandwichLibrary (you want the string). console.log(sandwichLibrary); // We can still use sandwichJS. console.log(sandwichJS.breads.white);
Then let's add a debugger statement right before we call noConflict:
// window.sandwichJS has an original value. window.sandwichLibrary = 'Library with books about sandwiches'; // SandwichJS loads. (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 }; // if librarySystem is undefined if (typeof librarySystem !== 'undefined') { // Handle librarySystem case. librarySystem('sandwichLibrary', function() { return sandwichLibrary; }); } else { // Handle window case. var oldSandwichLibrary = window.sandwichLibrary; sandwichLibrary.noConflict = function() { window.sandwichLibrary = oldSandwichLibrary; return sandwichLibrary; }; window.sandwichLibrary = sandwichLibrary; } })(); debugger; // This will reset window.sandwichLibrary to the original value. // .noConflict will also return the sandwichLibrary object. var sandwichJS = sandwichLibrary.noConflict(); // You want to print window.sandwichLibrary (you want the string). console.log(sandwichLibrary); // We can still use sandwichJS. console.log(sandwichJS.breads.white);
No in the debugger we can see that it resets window.sandwichLibrary to oldSandwichLibrary.
We can also see oldSandwichLibrary and sandwichLibrary (the object) in the Closure:
Then once we get to the end of our function, we can see in the debugger that we are, in fact, returning the sandwichLibrary object.
This is a technique used in a lot of javascript libraries, and it's important to be able to recognize it.
For example, jQuery has a noConflict method:
It's a little a bit different, but more or less the same idea.
Another example is in Underscore:
To be continued...