Professional Documents
Culture Documents
by P. LaNasa http://www.sitekickr.com/
Contents
1. Introduction 2. Fun with JavaScript Numbers and Prototype 3. Fun with JavaScript Strings and Prototype 4. Drop and give me 20 units: enhancing the numeric 5. Assertions are possible 6. Caching function return values 7. JavaScript organization for web development 8. Avoiding inline JavaScript 9. Using barricades for a more robust website 10. Bonus: Ad optimization leveraging JavaScript to improve RPM 11. Bonus: Optimize your JavaScript, save the environment
1. Introduction
I've been developing websites for over 10 years now, dabbling in almost every language and technology available, but also narrowing in on a handful of those that I find myself using on a daily basis. For many, that handful can be summed up in the popular acronym, LAMP (Linux, Apache, MySQL, Python/PHP/Perl). But, over the years, you really start to develop your own acronym that best describes your everyday development stack. For me, it's LAMCFJS (Linux, Apache, MySQL, ColdFusion, JavaScript). Of course, two of those don't fit the set, ColdFusion, because it requires a commercial license, and JavaScript because it's client-side. But that's my stack, and I'm stickin' to it. In recent years, I've developed a great fondness for JavaScript, and find myself blogging about it far more than any other topic. So, I thought I'd give this ebook thing a try, and piece together a few related posts. I hope you enjoy!
Number.prototype.safeDivideBy = function(x) { var result = this.valueOf() / x; if(result === Infinity) { return 0; } else { return result; } } > x = 10; > x.safeDivideBy(0); 0
Random numbers
I can't actually recall ever needing a random number between 0 and 1, but that's what JavaScript's random() function gives you. That's what any good random() function should give you. It's our job to build on that function for the specific needs of our application. In most cases, this means multiplying the result by a number, then rounding to an integer. Not bad, but this could be encapsulated into a function just as easily: Number.prototype.randomMax = function() { // return a random number with a maximum value of x return Math.floor(Math.random() * (this.valueOf() + 1)); } > x = 10 > x.randomMax(); 3
Existence in an array
If you've ever read "Code Complete" by Steve McConnell, you probably recall his discussion about Programming Into a Language. He was referring to the concept of tailoring a language to suit your development needs, rather than adjusting your needs to fit the limitations of the language. Prototype is one way we can achieve this within JavaScript. I used to do a fair amount of Python development, and came to love the in keyword syntax. It was so flexible in determining existence of one variable in a list or array. For example: 'test' in 'testing' 'test' in ['test', 'test1'] 9 in [9, 10, 11] I came to love it so much, that I just had to have it in my JavaScript development! Number.prototype.in = function(array) { var arrayLength = array.length; for(var i=0; i < arrayLength; i++) { if (array[i] === this.valueOf()) return true; } return false; } > myArray = [5,6,7,8] > x = 1 > x.in(myArray) false > x = 7 > x.in(myArray) true
Occurrences in a string
Sure, this one's easy. Just perform a RegEx match. But, to someone else reading your code, it might not be immediately apparent what you're trying to do.
myString = 'this is my string'; numTimes = myString.match(/is/g).length;
Drop that into a function on String's prototype and you've got more readable code at the very least: String.prototype.occurrences = function(substring) { var occRegEx = new RegExp(substring, 'g'); return this.match(occRegEx).length; } > myString.occurrences('is'); 2 Okay, that was not awesome. Hey, I'm just getting started!
Markup
How many times have you written or seen JavaScript like this, myself included: if(document.form1.name.value === '') { document.write('<b>Oh no, you forgot your name?</b>'); } All those gurus are always telling you to keep JavaScript out of your HTML, so shouldn't the reverse apply? But, I really want to just spit out a boldface message. Here's a cleaner way: String.prototype.bold = function() { return '<b>' + this + '</b>'; } document.write('Oh no, you forgot your name?'.bold());
Language
The previous couple functions walk the line of cohesion and usefulness, maybe even step over it. So, let's get back to the core of what a string is: typically a word or group of words in a given language. The String prototype is the perfect home for language functions. String.prototype.isQuestion = function() { var questionIdentifiers = ['when', 'where', 'why', 'what', 'who', 'how', 'can']; // does the string contain a question mark? if(this.indexOf('?') !== -1) { return true; } // search keyword may indicate a question without explicitly specifying the question mark for(var i = 0; i < questionIdentifiers.length; i++) { if(this.indexOf(questionIdentifiers[i]) !== -1) { return true; } } return false; } We could use our new function to refine a user's search by conditionally returning results more likely to answer questions, than to provide generic information: if(searchPhrase.isQuestion()){ // favor search results in the knowledge base } else { // favor search results in the product store }
Is it a Word?
There's an unfair bias towards the numeric in computer science. Of course, it all stems from the fact that the CPU speaks numeric, while we humans speak string. You can follow a gradual trend towards natural language-based syntax in popular development languages such as ColdFusion and Python. But, there really isn't much happening in terms of native support for natural language processing. I'm a little surprised by this, because I feel as if half of the applications that I develop require at least a very primitive form of natural language processing (search, form validation, speech recognition, etc.) Many languages offer isNumeric(), isDate(), or isArray() functions. But, how about isWord(), isVerb(), isCommand()? isCommand() could be used in searching algorithms (similar to isQuestion()) above. If a command is detected, the user might know more or less what they are looking for, otherwise, they may need a deeper level of assistance. isWord() could be used in form validation. If a user submits a field, validate that it's actually a word (or group of words). The definition of a word is subjective, but we'll make a few assumptions: 1. A word does not contain spaces 2. A word can contain only letters or the hyphen 3. A word is less than 25 characters in length String.prototype.isWord = function() { if(this.length > 25) { return false; } return /^[A-Za-z\-]*$/.test(this); } A user submits a form which has fields for first and last name, so you validate against our new isWord() function: if(!firstname.isWord()) { alert('Is that really your name?'); } Well, maybe it isn't. Many folks out there have a two-word first name. So, let's expand our String prototype to help out here: String.prototype.words = function() { return this.split(' '); } Now we are armed with the basics necessary to properly validate a first name: for each(item in firstname.words()) { if(!item.isWord()) { alert('Is that really your first name?'); break; } }
var oldProto = Number.prototype; Number = function(num, unit) { this.value = num; this.unit = unit; }; oldProto.valueOf = function() { return this.value } Number.prototype = oldProto; Number.prototype.setUnit = function(unit) { this.unit = unit; } Number.prototype.getUnit = function() { return this.unit; } The real magic of the code above is the recreation of the valueOf() function on the Number object. If you've read an opinions on creating numbers with the new keyword, you might have found them all to be against such a practice. But, much of their reasoning is that you loose the ability to perform operations on them. By redefining the valueOf function that JavaScript uses internally, we preserve that capability. However, it should be noted that the equality operation will no longer work! This is important to remember. x = new Number(30, 'inches'); y = new Number(30, 'inches'); x == y will return false. This is because the equality operator does an object comparison and does not use the valueOf() function. I'd have to say that this is probably the only downside to this method, we're forced to compare against the valueOf() method: x.valueOf() == y.valueOf(); Or, if we're feeling ambitious, adding a equals function to the Number object would serve our purposes equally well: x.equals(y); If you think this approach lends to code that's easier to read and write, using the code examples above, creating such a function should be relatively easy.
So, our new method of creating a Number object would look something like: x = new Number(30, Number.units.inches); But, what would prevent supplying string literals to the constructor? You could also perform some validation in the constructor: Number = function(num, unit) { this.value = num; var unitValidates = false; for each(value in Number.units) { if(value === unit) { unitValidates = true; break; } } // an assertion would make more sense here // unless we're accepting unit input from the user if(!unitValidates) { throw new Error(unit + ' unit is not allowed'); return false; } this.unit = unit; };
1. Modify the Number object itself Number.prototype.changeUnit = function(unit) { this.value = this.value * Number.unitMap[this.getUnit][unit]; } 2. Use a function to return the converted value Number.prototype.convertUnit = function(unit) { return this.value * Number.unitMap[this.getUnit][unit]; }
function Cache() { /* create the cache object as a singleton (only one instance allowed) */ if(typeof Cache.instance === 'undefined') { Cache.instance = this; } var data = [ ] // we'll abbreviate cacheAwareCall as caCall this.caCall = function(functionName) { var cacheCheck = this.load(functionName, this.caCall.arguments); if (typeof cacheCheck !== 'undefined') { return cacheCheck; else { var returnValue = window[functionName].apply(this, this.caCall.arguments) this.save(functionName, this.caCall.arguments, returnValue); return returnValue; } } this.save = function(functionName, argumentObject, returnValue) { // prepend item to cache data.unshift({ fname: functionName, arguments: argumentObject, returnValue: returnValue }); } this.load = function(functionName, argumentObject) { for(entry in data) { if(data[entry]['fname'] === functionName) { // we have a match on the function name // deepCompare is not implemented here, examples are throughout the web if(deepCompare(argumentObject, data[entry] ['arguments']) { return data[entry]['returnValue']; } } } return undefined; } return Cache.instance; }
Although, I was a little disappointed to find that the project explorer doesn't catch on to this function definition syntax: myMethod = function() { } So, functions defined in this way, within a "class", don't show up in the explorer view. If you need to develop on a remote server, and prefer to separate your JavaScript classes and packages into a folder/file hierarchy as is familiar to compiled languages, you're not outta luck. We just have to get creative! In traditional OO languages, we are accustomed to included individual classes within packages on a "need-to-use" basis, i.e. import mypackage.myclass In JavaScript, we're kinda stuck with the <script> tag, which is really only useful for loading in an entire JavaScript source file. That source file might contain 5 classes or 50 classes, all of which we must load, regardless of which ones we'll actually use. Because of this limitation, we typically include all of our classes in one big file. It doesn't have to be this way!
We can extend JavaScript a bit, including just a few lines of code to emulate the package/class include methods found in our traditional languages. This of course, requires a few capabilities: The ability to dynamically load JavaScript files. When I say dynamically, I don't mean "at runtime", because there's no compiler, everything is at runtime. What I'm talking about is the ability to load a JavaScript file from within JavaScript. We know we can do this by dynamically inserting a <script> tag into our document, and providing it the appropriate src attribute. The ability to determine if a class has already been loaded, so we don't duplicate any efforts. This is possible with the JavaScript typeof operator. For our cases, we need to introduce the eval function, since we're dealing with a dynamic function name. A naming convention, so we know which JavaScript file to load, based on which package/class name was provided. By sticking to the convention of the filename carrying the same name as the class, and the folder carrying the same name as the package, our little loader will always know which file to grab. As Nicholas Zakas points out, we can encapsulate the loading of an external JavaScript file into a function, and provide a callback to execute after the script has loaded, so our method call might look like: loadScript("/mypackage/myclass.js", function(){ //initialization code }); Now, if we alter his script a bit, to use the naming convention we suggested above, the loadScript function would be more like:
function loadScript(package_class, callback){ var className = package_class.split('.').pop(); if(eval('typeof ' + className) !== 'undefined'){ callback(); } var script = document.createElement("script") script.type = "text/javascript"; if (script.readyState){ //IE script.onreadystatechange = function(){ if (script.readyState == "loaded" || script.readyState == "complete"){ script.onreadystatechange = null; callback(); } }; } else { //Others script.onload = function(){ callback(); }; } script.src = '/' + package_class.replace(/\./g, '/') + '.js'; document.getElementsByTagName("head")[0].appendChild(script); }
Essentially, we are replacing the dot notation with folder notation, so by calling this method: loadScript('Mypackage.Myclass', function(){ //initialization code });
We are actually loading the /Mypackage/Myclass.js file. The Myclass.js file contains a Myclass function, which upholds our naming convention. Notice also, in the opening lines of the new loadScript method, we check to see if the class has already been loaded. But, what if we want to load multiple classes, and have our callback fire only after all of the external scripts for those classes have been loaded? One possibility would be to create an array of script elements, load them all, then with each onload event, check to see if all scripts have been loaded:
function loadScripts(packages_classes, callback){ var scripts = [], scriptCount = packages_classes.length; for(var i = 0; i < scriptCount; i++ { var className = package_class.split('.').pop(); if(eval('typeof ' + className) !== 'undefined'){ scripts[i] = {loaded: true}; } else { scripts[i] = {loaded: false}; } scripts[i].script = document.createElement("script") scripts[i].script.type = "text/javascript"; if (scripts[i].script.readyState){ //IE scripts[i].script.onreadystatechange = function(){ if (scripts[i].script.readyState == "loaded" || scripts[i].script.readyState == "complete"){ scripts[i].script.onreadystatechange = null; scripts[i].loaded = true; if(checkAllLoaded()) { callback(); } } }; } else { //Others scripts[i].script.onload = function(){ scripts[i].loaded = true; if(checkAllLoaded()) { callback(); } }; } scripts[i].script.src = '/' + package_class.replace(/\./g, '/') + '.js'; document.getElementsByTagName("head") [0].appendChild(scripts[i].script); }
// private function - check if all scripts have been loaded var checkAllLoaded = function() { for(var i = 0; i < scriptCount; i++ { if(!scripts[i].loaded) { return false; } else { return true; } } } } I have not tested the above code, at all! It's meant to be more theoretical. But, it would appear that loading multiple external files, and waiting for a callback is possible. So, a method like this, very clean and consise is actually possible in JavaScript!
loadScripts(['Mypackage.Myclass', 'Mypackage.Myclass2', 'Myotherpackage.Myotherclass'], function(){ // tie it all together here! });
Then, at some point down the road, you decide that green isn't really the look you were going for with page titles, you need to manually adjust all of your HTML files, to make the inline style adjustment. This difficulty is compounded with JavaScript, as often times your editor's global file search/replace feature will be difficult to use against a multiline search. 3. Future-proofing <script> tags currently validate inside elements (with XHTML you need to use the ugly //! [CDATA[ trick), but there may be a time when they don't, as the <style> currently does not. 4. Context During a presidential race, we hear phrases spoken by candidates, but out of context. The phrase may have carried meaning in the context of the content that surrounded the phrase, but alone, it carries no meaning or just makes them look bad. The same happens with CSS! To illustrate, let's say we have a block of content that we want to emphasize heavily by placing a 50 pixel black border around it, using inline CSS: <div style="border: 50px solid #000;">This content is so important that mobile users need to suffer!</div> This may present well on a 1024 pixel wide desktop browser, but on a mobile device, the border itself will take up nearly half the screen. To avoid this, we would typically have separate CSS files for mobile and desktop. But, there is no such separation when we use inline CSS. To illustrate in a different way, let's say we have the following inline CSS: <a style="color: #a00;">This is a red link</a> If this content is loaded on a device with a black & white screen, the red link styling is useless and only served to make the content itself take longer to download. 5. Team environment If you work in a team, it's likely that you have one team member dedicated to content (HTML), another to style (CSS), and still another to interactivity (JavaScript). If you are absent version control and a "check-in" procedure as so many small operations are, the logical course of action is to restrict access to HTML files to the person who edits the HTML, CSS files to the person who manages the CSS, etc. This serves to prevent one person from overwriting another's work if they are both accessing the same file at the same time. That system works well, unless you have CSS and JavaScript embedded in your HTML files. In this scenario, the person who manages CSS needs to say, "Hey Bob, can you close the file your working on for 20 minutes while I edit the inline CSS? When I'm done, don't forget to load the file from the server again so you don't overwrite my changes. Thanks Bob, you're the best, sorry for wasting 20 minutes of your day!"
6. Markup to content ratio (bad for search engines) This one has been preached about by SEO wizards for years, so I apologize for duplicating it here. However, the list wouldn't be complete without it. Not only does inline style and script increase page load time, it increases the download time for search engines who are just looking for raw content. In effect, a search engine has downloaded additional information that it needs to discard anyway. This negatively affects how search engines position your site in two ways: The content vs. markup ratio. It has been suggested the search engines frown when the amount of markup exceeds the amount of content by an undetermined amount (the ratio, of course, being dependent on the search engine). Without knowing the ideal ratio, it's a wise practice to keep markup to the absolute minimum required. This includes, of course, eliminating inline style and script when possible. Page load. Recent evidence suggests that Google (and perhaps other SEs) factors your page load time into it's ranking algorithm. Essentially, with all other things being equal, a site with a faster page load will be ranked higher. As touched upon in the point above, inline style and script increase page load time. 7. Performance We've touched on how inline style and script can negatively affect performance, but there's another side to that. Housing all of your CSS and JavaScript in external files can actually improve performance drastically. The magic comes from browser caching. A typical web site frequently updates content, but rarely touches style and interactive areas. With this in mind, most web hosting configurations are set to direct web browsers to cache CSS and JavaScript files. By caching these files in the browser, they do not require another HTTP connection to download them from the server. This post goes into more detail on browser caching. With these seven reasons comes a compelling argument against the use of inline style and script, but by no means am I saying it is incorrect to do so. It is currently valid, and in very simple sites, may actually improve performance. In some cases, you can apply an inline style without coming in conflict with any of the reasons above. But, avoiding inline style and script usually leads to many years of happy and healthy web coding!
Server-Side
On the server side, the first step is to decide what the barricade protects. For instance, if you have a two-column website in which both columns are dynamically generated by your server-side code, you might want the content column to be entirely unaffected by any errors that occur in the sidebar. A popular example of this issue occurs within any given WordPress blog. Depending on your PHP error settings, an error in your sidebar will "take down" the entire page. With this possibility, it's in your user's best interest to barricade the sidebar from the rest of the site. For that matter, perhaps the footer as well. We can do this with our old friend the try/catch block. By wrapping the entire sidebar code in a try/catch, we prevent any and all errors from affecting the rest of the site. This still allows our more "fine-tuned" errors to do their job of properly logging and notifying the user of any errors. But, we also have the added comfort of being able to log errors from within the "master" catch block. This doesn't even mention the level of security gained by not exposing error information to potential hackers.
JavaScript
It seems like every time I come up with a good, solid server-side methodology, it doesn't transfer to JavaScript! With JavaScript, errors are not displayed front-and-center to the user. They are tucked away behind a small warning icon in the corner of the browser. But, that doesn't mean they can't do just as much harm. As with server-side code, one error in your script and that's it, execution stops. Of course, we have the same try/catch capability. We could easily wrap an entire chunk of JavaScript in a nice big try/catch. But, how many of us want to wrap our entire script in a try/catch? JavaScript just doesn't have the useful error reporting that server-side languages have, such as context, line number, etc (some browsers support line number). This would be a debugging nightmare, as the errors would no longer be picked up by our browser console our true friend in the debugging effort. JavaScript does, however, treat it's "turn-me-on" tag a little differently than server-side-languages. "turn-me-on", of course, refers to the <script> tag saying to the browser, "start parsing JavaScript!". What were you thinking? The <script> tag is self-contained, error-wise. It is an error barricade from the rest of the <script> tags. Server languages like PHP or ColdFusion don't provide such a barricade. You can have 30 <? ?> directives in one PHP file, even divide them up across multiple files. But an error in one ruins the whole batch. Whereas in JavaScript, an error in one <script> tag kills the entire script within, but doesn't affect the other <script> tags at all. So, how is this useful? <script type="text/javascript"> var x = something; // 'something' is not defined document.write('Why won\'t my message display, sadness'); </script> <script type="text/javascript"> pacemaker.keepOnWorking(); document.write('I am unscathed by the errors in my document\'s other script tag!'); </script>
$(window).scroll(function(){ if(siteKickr.isScrolledIntoView($('.entry-content p').first().next().next())) { if($('#post-inline-ad').hasClass('sidebar')) { // if ad is in sidebar, move back to paragraph $('#post-inline-ad').removeClass('sidebar'); $('.entry-content p').first().next().next().after($ ('#post-inline-ad')); } } else { if(!$('#post-inline-ad').hasClass('sidebar')) { // if ad is not in sidebar, move to sidebar $('#post-inline-ad').addClass('sidebar'); $('.widget-container').last().after($('#post-inlinead')); } } }); siteKickr = { isScrolledIntoView: function(elem) { var docViewTop = $(window).scrollTop(); var docViewBottom = docViewTop + $(window).height(); var elemTop = elem.offset().top; var elemBottom = elemTop + elem.height(); return ((elemBottom <= docViewBottom) && (elemTop >= docViewTop)); } } This method isn't quite as hardcore as keeping the advertisement in the view regardless of scroll position, but it gives the user another opportunity to view the ad, where it may previously had not been in a recognizable position.
Then, let's again put JavaScript to work for us, in this case to reveal an advertisement in the sidebar after the user has been on the page for 60 seconds: The HTML <div id="timed-ad"> <span class="close">X</span> <p>Thanks for your interest in this article. How about a break to check out the awesome deals from our friends at: <a href="http://www.jdoqocy.com/click-6324420-10865503" target="_blank"><img src="http://www.ftjcfx.com/image-632442010865503" width="234" height="60" alt="" /></a></p> </div>
The CSS #timed-ad { position: fixed; top: 300px; right: -270px; /* account for width and padding */ width: 250px; | padding: 0 10px; border: 1px solid #000; border-radius: 5px 0 0 5px; background: #eee; } #timed-ad .close { float: right; margin: 0 -10px 0 0; padding: 5px 8px; color: #fff; background: #555; cursor: pointer; } The JavaScript (jQuery) $('#timed-ad .close').click(function() { $(this).parent().remove(); }); setTimeout(function() { $('#timed-ad').animate({ right: -1 }, 2000); }, 60000); // 60 seconds
The implementation of this method is simpler than the other methods, so I'm going to break my own rules of separating content, css & script, and package it all up into a small piece of jQuery: $(window).resize(function() { if($(window).width() > 1400) { /* widescreen - show gutter ad */ if($('#left-gutter-ad').length === 0) { $('body').append('<div id="left-gutter-ad" style="position: fixed; top: 62px; left: 15px;">My ad code goes here</div>'); } } else { $('#left-gutter-ad').remove(); } }); $(document).ready(function() { $(window).resize(); });
Plants
Coal was formed from the remains of vegetation that grew millions of years ago. The plants which formed coal captured energy from the sun through photosynthesis. This energy is used to create the elements within plant tissue. The element most important for us being carbon, which gives coal most of its energy. No environmentally negative factors here.
Coal
Of course, coal is not the only form of energy used to generate electricity, but it's the largest in the US. Coal power stations have machines that convert the heat energy from the combustion of coal into mechanical energy, which then powers the electric generator. It's the byproducts and wasted heat energy from this process that causes the negative environmental effects. The process is not 100% efficient. Those inefficiencies aren't bottled up, they're sent to our air, lakes and rivers. On average, a ton of coal generates 2,460 kilowatt-hours of electricity. Let's make that simpler: 1 kilowatt-hour of electricy = .81 pounds of coal
The Kilowatt
We Americans aren't fluent in the metric system, but we know the Kilowatt better than any other unit of measurement when it comes to electricity. Why? Because that's what we see on our electric bill, the Kilowatt-hour. But, lets not forget that the kilowatt-hours we use at home don't directly translate to the kilowatt-hours generated at the power plant. A certain amount of energy is lost in the transmission over the power lines. So, how does this translate to our computer's CPU power demands? A kilowatt-hour may be enough to power your light bulb for an entire day, but a CPU is vastly more complex. A light bulb performs one operation consistently, from the time you turn it on, to the time you turn it off. Calculating it's energy usage is straightforward. But, your computer's processor is more dynamic.
The CPU
CPU's used to maintain a more steady power consumption, but today's CPU is more energy conscious when idle. The big question is, "how much energy is saved when it's idle, compared to when it's working?" Apparently, this is not an easy question. It, of course, depends on who you ask, what the rest of the system is doing, and which processor. Many forums I visited seemed to suggest that an Intel I7 would drop to 75 watts, from 95 watts when at idle. I also had difficulty finding out exactly what idle means. Is the processor idle between each sequential instruction sent its way? Or does it require a length of time before it settles down into it's idle state? Again, a solid conclusion wasn't reached. So, for this article, I'm going to make the following assumptions: The difference between idle and working = 20 watts = .02 kilowatts Additional Kilowatts used for 1 CPU instruction = the number of hours the instruction takes * .02 I'm emphasizing the word additional, because the CPU uses power whether it's processing an instruction or not. The next question: How long does a CPU take to execute one instruction? It varies of course, so let's again use the Intel I7. Wikipedia says 82 billion instructions per second! You might be thinking, why are we possibly concerned with power consumption when we can process 14.7 quadrillion instructions for just one measly kilowatt-hour! 1 Kilowatt-hour, we remember, is created by the combustion of .81 pounds of coal. 1 pound of coal gets us 18.2 quadrillion instructions. Good job coal, your not so bad after all. But, hold on, we haven't worked our way through the remaining levels of abstraction. How much does 18.2 quadrillion instructions really buy us?
JavaScript
Huh, how did I get to JavaScript? I skipped machine code, assembly language, the OS, the browser environment. I decided to do this for the dramatic effect of showing how many CPU instructions are required for a simple JavaScript statement. var x = 1 + 1; I'm now thinking to myself, "how do I begin to determine how many machine code instructions that little statement took?" I intentionally avoided anything user-interface or input related, to simplify this task. We first need to find out what JavaScript "sits on". Until Google's V8 engine, JavaScript was interpreted, and still is in most browsers, by a JavaScript engine. The first JavaScript engine, SpiderMonkey, was written in C++.
C++
What does it take for C++ to run a JavaScript statement such as the one above. I'd have to imagine that this particular statement is one-to-one. C++ allows us to declare and initialize a variable in one line of code, even if that initialization involves arithmetic. int a = 1 + 1;
Assembly Language
.data x: .word y: .word a: .word add a,x add a,y int 21h 1 1 0
Is the above proper assembly langage? No. Will it work? Probably not. I don't know Assembly Language, I just wanted to illustrate the number of instructions required to perform a simple operation. In this case, it's likely that each assembly code instruction will produce one line of machine code. So, the most basic JavaScript statement might translate to at least 7 lines of machine code. I'm talking about a statement that involves nothing but the processor and RAM (no display, peripherals, disk drive, etc). A more complex statement, such as document.write(), may actually consist of hundreds of machine instructions. So, we can easily see how inefficient JavaScript code is orders of magnitude more inefficient when you look at it from a machine code standpoint. But this still doesn't really mean anything. It's too difficult to relate additional lines of machine code directly to environmental impact! But, what if we convert that into more recognizable metric, time. If a set of poorly optimized JavaScript statements takes 100 milliseconds to run, and the optimized version of that same code takes 50 milliseconds to run, you are keeping that processor busy for 50 milliseconds beyond what is necessary. 50 milliseconds = .05 seconds = .000014 hours .000014 hours * .02 additional kilowatt hours required by the active CPU = .00000028 kilowatt-hours So, an additional .00000028 kilowatt-hours are required by your one inefficient algorithm. Not bad, that only means .00000022 pounds of coal. But, that's just one algorithm, in one page load, by one user. If your website gets 10,000 page loads today, that number hops up to .0022 pounds of coal wasted. Yikes, that's getting a little scary! Over a year, that turns right back into that original .81 pounds of coal, enough to generate 1 kilowatt-hour of electricity, to power the additional CPU cycles required by that same inefficient algorithm. Again, that's just ONE algorithm, on one page on the web. Now, my brain is exhausted from all this math, but multiply this by the number of pages on the web, and the number of poorly written algorithms on a page, and you've got an awful lot of coal! Who votes that the windows task manager should have another column for "Coal Burned"!