JavaScript Lessons From the Flurry Trenches

June 6, 2013
|
Kenny Lee
Tumblr Facebook Twitter Google+ LinkedIn Email Email

The early 2000s was an interesting time for the internet.  Windows XP and Internet Explorer 6 dominated in market share.  A young, dashing, intrepid, future engineer was just learning the ways of the web.  His tools of trade: analyzing scripts from DynamicDrive that made falling snowflakes and browsing partially constructed websites hosted by GeoCities.

90sScreenShot

Looking back now, I cannot believe all the misconceptions I picked up about JavaScript by trying to learn the language from such poor examples.  Initially, I thought of it as nothing but a simple scripting language for making silly effects.  Since then, both the internet and I have matured.  The average level of JavaScript expertise being published has greatly increased and I have cleared up those misconceptions.  The times when I have made the greatest gains in understanding JavaScript have come from investigating the more difficult nuances of the language.  Hopefully, by discussing some of these details, I can capture your curiosity and motivate further learning.

These details about JavaScript might seem very complex to a beginner.  Honestly, most of the things you need to do on a daily basis as a frontend engineer do not require intimate knowledge of these topics.  However, working at Flurry, over the course of many years, I have encountered several problems where knowing these details have allowed me to come up with much better solutions than if I were fumbling around blindly.  A good craftsman should not only never blame his tools, but should also understand as much about how they can be used as possible.

Objects, Primitives, and Literals, oh my!

There seems to be some confusion as to whether primitives exist in Javascript or if everything is an object.  The easiest ones to rule out are null and undefined.

The three exceptions are strings, booleans, and numeric values.  These can be explicitly created as objects using String, Boolean, and Number objects. However, if variables are defined using literals (e.g. “foo”, true, 3), they will contain an immutable primitive value.

These values will be automatically cast as the object version of the primitive when necessary, allowing for the object methods to be referenced.  The object is immediately discarded after use. In this example, I will use Java-like syntax to explain what is going on behind the scenes.  In this pseudocode, assume there exists a primitive type string and a corresponding String object just like Java’s.

//Javascript Version       //"Java" version
var primitive = "foo";     string primitive = 3;

primitive.charAt(0);           ((String) primitive).charAt(0);
//f                                      //the object created by casting is 
                                          //not saved anywhere

primitive.someProp = 3;    ((String) primitive).someProp = 3;
                                          //the object created by casting is not 
                                          //saved anywhere and neither is someProp

alert(primitive.someProp); alert(((String) primitive).someProp);
//undefined                       //someProp does not exist on primitive 
                                         //because it is a primitive value and not 
                                         //an object

 

To the casual observer, it might seem like these literals are created as Objects because they react to all the expected method calls that the Object versions of the primitives would.  The automatic casting built into JavaScript because of its nature as a weakly-typed language assist in perpetuating this illusion.

Further Reading:
Mozilla JavaScript Reference: Primitives and Objects
JavaScript Garden: Objects

Passing by Reference…JavaScript can do that? Nope.

Modern languages abstract a lot of memory management complexity away from the user.  Anyone who has learned about pointers in C/C++ would likely find this a helpful feature for the general case. However, it is still important to understand the basics of what is being abstracted away from you.

See this little snippet of code:

function change(primitive, obj1, obj2){
  primitive = 1;
  obj1.prop = 1;
  obj2 = {prop : 1};

  console.log(primitive); //1
  console.log(obj1);  //{prop : 1}
  console.log(obj2);  //{prop : 1}

  obj2.prop = 2;
  console.log(obj2);  //{prop : 2}
}

var a = 0,
   b = {},
   c = {prop : 0};

change(a,b,c);

console.log(a);      //0
console.log(b);      //{prop : 1}
console.log(c);      //{prop : 0}

 

If you read the comments about the output of that code, you might be a little surprised to see that c did not change after the function call.  This is because JavaScript is much like Java in that it only passes arguments by value and secretly uses pointers to pass object arguments around.

The primitive/a argument is simply passed by value, which means that a copy of the value in a is used by the function change when dealing with primitive.  None of the changes to primitive are propagated to a.

Now, if we look at the obj1/b example, you might make a claim like “JavaScript supports pass-by-reference for objects”.  I certainly did for a long time before I realized what was actually happening.  So, if obj1 is a copy of a value, what is that value?  It is a copy of b, which is really a pointer and contains the address in memory where the instance of b is stored.

In C/C++, the developer must be cognizant of whether some variable is an object or pointer.  There are different operators for each case, the dot and arrow operators.  The dot is used for directly accessing an object’s methods.  The arrow is used for accessing a method of pointer to an object.  In JavaScript (and Java), there is only one such operator, the dot.  This is because there is no way to separately refer to an object and a reference or pointer to an object.  Every time you think you are dealing with an object in JavaScript, you are using a pointer to that object, but all of that complexity is abstracted away from you as a developer.

Now that we (hopefully) understand that very, very intricate concept, we can explain what is happening with obj2/c.  Because obj2 is copy of the memory address of c, we can operate on it like we would any other object in the scope of change.  However, when we assign obj2 with the object literal {prop : 1}, we are overwriting the memory address used by obj2 and performing further operations on that new memory address.  c still references its original memory address and that is why it is unchanged outside of the change function.

Further Reading:
Details on Java’s Pass-By-Value

Prototypes, All the Way Down

JavaScript is very unique amongst the popular programming languages today in its use of prototypal inheritance chains.  Every object in JavaScript has a prototype that is a reference to an instance of another object until the chain reaches the built-in Object.prototype.  Each object instance copies only the subset of properties and methods for that object and relies on a pre-existing instance for the things that need to be inherited.  When a call to a method is made on an object, the engine will go up the prototype chain looking for that method.  This can present advantages like memory savings and disadvantages like creating unexpected conflicts down the line when changing the prototype of some object higher up the chain.

There are many good articles out there that will delve deeper into JavaScript prototypes.  What I find interesting is how the new keyword works in this context.  When new is used in front of a function, that function becomes a constructor for a new object whose prototype is that function’s prototype.  Remember, functions are first-class citizens in JavaScript and behave very similarly to objects.

Essentially var x = new foo(); becomes

var x = {};
x.constructor = foo; 	     //done internally, not actually writable by
			     //the developer in the same manner
x.prototype = foo.prototype; //some browsers access this via __proto__
foo.call(x); 		     //foo is called with x placed in the execution context (this)

 

This behavior where new is actually creating an object before calling the function is important for understanding how the this keyword works.

this, very, very simply put, refers to the execution context a function is being called from.  An execution context can be thought of as the object a function is called on.  The global scope is also an object, window.

Below are all the different values this might have in different contexts.

//this == window;
(function(){
	//this == window;
	var x; 		//in the function scope
})();

var obj = new function(){
	//this == obj;
}();

var obj2 = {
	fn : function(){
		//this == obj2
	}
}

var y = {};
(function() {
	//this == y
}).call(y);

 

Because of all the different values this can have, a lot of developers will save references to the this context with their own variables like self, that, _this.  While I think this practice can be very useful, I would recommend more explicit variable names that actually describe what the context is supposed to represent.

Further Reading:
Mozilla JavaScript Reference: How prototype Works
Mozilla JavaScript Reference: How call Works
JavaScript Garden: this

Closing it Out with Closures

JavaScript supports function scope but not block scope.  This is a departure from most languages.  A scope is usually a block of code enclosed by some braces; variables declared in that scope will not exist outside of that scope.  In JavaScript, if/while/for blocks will leak variables declared in their blocks to their parent scope.

Here is some code that captures this little gotcha.

var projectIds = [1,2,3,4]
    index = 0,
    size = projectIds.length
;

for ( ; index < size ; index++) {
	var projectLink = getProjectLink(index);
	projectLink.on("click", function(){
		makeAjaxRequest("/getProjectDetails.do?projectId=" + projectIds[index]);
	});
	addToPage(projectLink);
}

 

This code snippet will cause each project link to fetch the details forprojectId=5.

Why?  The for loop puts index in the global scope and, after it runs, index will persist with a value of 5.  Because the click handler runs in the global scope and after the completion of the for loop, it will use index=5.  Alternatively, if this code were enclosed by some function, the click handler would throw an error because it wouldn’t find index or some random value set by some other function that put index in the global scope.

However, if we slightly modify the code, we can make this work as intended.

(function(){
	var projectIds = [1,2,3,4]
	    index = 0,
	    size = projectIds.length
	;

	var addClickHandler = function(link, projectId){
		link.on(“click”, function(e){
			makeAjaxRequest("/getProjectDetails.do?projectId="
				 + projectId);
		});
	};

	for ( ; index < size ; index++) {
		var projectLink = getProjectLink(index);
		addClickHandler(projectLink, projectIds[index]);
		addToPage(projectLink);
	}
})();

 

Why does this work? Closures.

A closure is a combination of a function and its referencing environment.  Because JavaScript only has function scope, this means that all functions are closures and always have access to their outer scope.

In this example, when addClickHandler is called, it creates a function with a copy the value of projectId in its scope.  The click handler will now access that reference but the for loop will not change it.

Further Reading:
Douglas Crockford’s JavaScript: The Good Parts section on closures