Learnings on javascript scope chains, object prototype hierarchies, javascript "inheritance" and "constructors"
I had to debug some very funny stuff going on in some of my models yesterday/this morning. It lead to some learnings about the internals of javascript inheritance and scope. Please excuse my run-ons and bad grammar!
First one of the interesting things about javascript is that variables can be declared locally inside a function and that function can be turned into an object using the “new” keyword but those local variables are not available to the object that the new keyword creates out of the function!
Jesse = function() { var name = jesse };jesse = new Jesse();typeof
However if you use the this object within the context of the “constructor” it does work. The reason being that the “new” keyword creates a new object then inserts that object into the body of the “constructor” function’s “scope chain” with the property name “this” that way any references to “this” within the constructor function reference the “new object” and not the this of any scope surrounding the constructor function or the call to the “new” keyword.
So if you were to think of the scope chain as an array like data structure where “global” aka ”window” scope is normally the last element in the array aka = arrayarray.length
AND if you were to imagine the behavior of the javascript interpreter trying to de-reference a property as a walk through the array from element 0 to element array.length looking at every property on every object in each “cell” of the array and checking if their property names match the one in question.
Then the new keyword takes a blank object assigns the property name this to it and sticks it at the front of the aforementioned scope chain array and then evaluates the contents of the constructor using it.
Functions like call/apply/bind and the keyword “with” can be used to manipulate the scope chain programatically. However call/apply/bind can only manipulate the “this” property name and the “with” keyword comes with many caveats and performance hits.
In my opinion call/apply/bind are probably all shortcuts to simply creating a closure around a function with a pointer to the current “this”.
Here is an example:
var Jesse = function(){};var jesse = new Jesse();Â //Look ma we now have an object with a reference to itself called "this"jesse.firstName = "JESSE";Â //aka jesse.lastName = "SANFORD";jesse.getFirstName = function() { return this.firstName; }; //yup you guessed it "this" is pointing at jessejesse.getLastName = function() { return this.lastName; };//now here comes the fun!var tom = { name: "TOM" }; //look ma another object! except this is an "object literal" who cares you say?... I agree I don't care... yet... see below.jesse.getFirstName.call(tom); //yup this will return "TOM"jesse.getFirstName.bind(tom)(); //same as before! //note the extra parens at the end are simply calling the function that is returned by the bind object which is simply the jesse.getFirstName function with the scope chain modified so that the reference to "this" is replaced with tom
Note that all of the above examples are very trivial and they are in the imperative style so they may not seem that handy. But they can also be used when doing asynchronous callback passing functional style
//lets create a new function called printName which takes in two callback getter functions and print's their return values;jesse.printName = function(fcb,lcb) { console.log(fcb()+" "+lcb()) };//now you might expect the following to work:jesse.printName(jesse.getFirstName, jesse.getLastName); //undefined undefined//but it doesn't because now "this" points to the GLOBAL scope! //check it out:firstName = "global"; //note the lack of the use of var (pushing the variable into global scope)Jesse = function(){};Jesse.firstName = "parentobj";Jesse.prototype.firstName = "prototype";jesse = new Jesse();jesse.firstName = "instance";jesse.print(cb) { console.log(cb()) }; //just print whatever the callback returnsjesse.getFirstName = function() { return this.firstName }; //supposed to return the instance's foo variablejesse.print(jesse.getFirstName); // 'global' !!! WHAT? :)
This may not be news to you but scope and functional programming can get wonky! So this is where the call and bind functions from above come in handy!
If we were to call the jesse.printName(fcb, lcb) function above with them we would get what we expected rather than “undefined undefined”
jesse.printName(jesse.getFirstName.bind(jesse), jesse.getLastName.bind(jesse)); //"JESSE SANFORD"
OR if we re-wrote the printName function we could do the same with call (daisy chaining the passing of “this”)
jesse.printName =Â Â function(fcb,lcb) { console.log(fcb.call(this)+" "+lcb.call(this)) };jesse.printName.call(jesse, jesse.getFirstName, jesse.getLastName);Â //"JESSE SANFORD"
But bind/call/apply may not look as clean as you might like. Also manipulating the scope chain just feels a little wrong to me. SO in some situations it makes more sense to use closures!
let’s re-define our getFirstName and getLastName functions to remove the “this” from them. Instead let’s have them reference a synonym called “self”
jesse.getFirstName = function() { return self.firstName; };Â jesse.getLastName = function() { return self.lastName; };
but what is self you say? it’s whatever you make it!
check this out…
var self = jesse;jesse.printName(jesse.getFirstName,jesse.getLastName); //"JESSE SANFORD" !!Yes because now the interpreter when running through the blocks for getFirstName and getLastName will search the scope chain for a variable called “self” and the first one it finds will be used within them.
Again this example is very trivial and may not seem very useful. But think about it in reference to objects and their constructors. Remember a constructor when called ALWAYS has “this” pointed at it’s new blank object instance. SO…
Let’s re-factor the Jesse parent object like so:
//We can also use the inheritance to define the Jesse parent obj more robustly for re-use… Hell let’s just rename it to Person as you might have expected.
var Person = function() { var self = this; //now self will point at WHATEVER new instance object is constructed by the "new" keyword when this "constructor" is called.  self.getFirstName = function() { return self.firstName; };   self.getLastName = function() { return self.lastName; };  self.printName = function(fcb,lcb) { console.log(fcb()+" "+lcb()) };};//now we can simply instantiate a new Person as jesse, name it and print the name!jesse = new Person();jesse.firstName = "JESSE";jesse.lastName = "SANFORD";jesse.printName(jesse.getFirstName, jesse.getLastName); //"JESSE SANFORD"!!!
NOTE: You might expect the following to work: (If you are confused as to why not… see the first example all the way at the top!)
var Person = function() {Â Â var self = this;}Person.prototype.getFirstName = function() { return self.firstName; };Â Person.prototype.getLastName = function() { return self.lastName; };Person.prototype.printName = function(fcb,lcb) { console.log(fcb()+" "+lcb()) };jesse = new Person();jesse.firstName = “JESSE”;
jesse.lastName = “SANFORD”;
jesse.printName(jesse.getFirstName, jesse.getLastName); //ReferenceError: self is not defined (Because self is only available to the instance… NOT to the prototype which is where getFirstName and getLastName are defined)
THIS ALSO WILL NOT WORKÂ ( again If you are confused as to why not… see the first example all the way at the top!)
var Person = function() {Â Â Â var self = this;Â };//if we construct a new jessejesse = new Person();jesse.firstName = "JESSE";jesse.lastName = "SANFORD";//and define the following as above:jesse.getFirstName = function() { return self.firstName; };Â jesse.getLastName = function() { return self.lastName; };jesse.printName = function(fcb,lcb) { console.log(fcb()+" "+lcb()) };//we get as expected:jesse.printName(jesse.getFirstName, jesse.getLastName); //ReferenceError:Â self is not defined (as above)
A quick (albeit dangerous) fix to the above examples would be to use the “with” keyword and a local variable called self.
var whoami = {self: jesse};with(whoami) {Â Â jesse.printName = function(fcb,lcb) { console.log(fcb()+" "+lcb()) };Â //"JESSE SANFORD"!!!}However “with” is dangerous and slow and almost everyone regards it as something you should never use. The problem is what happens if you don’t define “whoami” correctly RIGHT before you call “with” keyword. Will you remember WHERE you defined it? Will you have even been the one who defined it? “with” will cause the interpreter to walk the scope chain looking for properties on variables that match the variable names referenced within the block following it’s use. This can cause variables from all different scopes to get used and as such very unpredictable output! It get’s even worse if you redefine variables within the with block. You may try to set a value of a property within “whoami” and very well be creating new GLOBAL variables if you are not careful to make sure all variable names used are already contained within “whoami” as properties. See:Â http://stackoverflow.com/questions/61552/are-there-legitimate-uses-for-javascripts-with-statement/61676#61676
A safer alternative is in the works for javascript 1.7 using the “let” keyword
let(self = jesse) {Â Â jesse.printName = function(fcb,lcb) { console.log(fcb()+" "+lcb()) };Â //"JESSE SANFORD"!!!}Of course there are not many interpreters for 1.7 yet though. (The above does not work in the current version of node.)
A couple of other interesting notes creating objects/constructors and on prototype chains and when you can access them. (see note above about object literals)
Basically if you create an object literal… you will never get access to it’s prototype chain UNLESS you modify the “Object” object’s prototype. This is the super-prototype in javascript. The only thing higher up in the order than Object is Object.prototype and then there is null. SO don’t expect this to work:
var tom = { firstName: "tom", lastName:"pytleski" };tom.prototype.getLastName = function() { return this.lastName; }; // returns TypeError: Cannot set property 'getLastName' of undefined (because tom has no prototype! it's an object literal... yeah now we care :)//However we can do this:Object.prototype.getFirstName = function() { return this.firstName; };tom.firstName = "tom"; tom.getFirstName(); //'tom'
But that is BAD! because you just added a getFirstName function to every object in your virtual machine.
var one = new Number();one.firstName = "two"; // funny huh :)one.getFirstName(); //'two' // great now numbers have a getFirstName function!
So the correct way is definitely to create a constructor first. Even a blank constructor works:
var Tom = function(){};Â tom = new Tom();tom.firstName = "tom"; //'tom'
BUT remember that the instance does NOT have a prototype chain. Trying the following will not work:
tom.prototype.getFirstName = function() { return this.firstName; }; //TypeError: Cannot set property 'geFirstName' of undefined (because instances of objects don't have prototypes!)
However as you have seen above this does work:
Tom.prototype.getFirstName = function() { return this.firstName; };//and now:tom.getFirstName(); // 'tom' //yes as expected.
Finally it may help to setup instance variables within the constructor right off the bat.
var Tom = function() { this.firstName = "jesse"; this.lastName = "sanford"; };Tom.prototype.getFirstName = function() { return this.firstName };tom = new Tom();tom.firstName = "tom"; //you can set their values just as you might expecttom.getFirstName(); //'tom' //yup!
A note on inheritance… Everyone has probably figured this out already but I am repeating it here at the end if it’s still not quite clear. The prototype heirarchy provides the single parent inheritance you have seen.
Person = function(){};jesse = new Person();tom = new Person();Person.prototype.getFirstName = function() { return this.firstName; };jesse.firstName = "jesse";jesse.getFirstName(); //'jesse'tom.getFirstName(); //'jesse' yup!tom.lastName = "pytleski";Person.prototype.getLastName = function() { return this.lastName; };Person.prototype.lastName = "sanford";tom.getLastName(); //'pytleski' yup! //Also note that the hierarchy is respected when the scope chain is inspected by the interpreter as expectedjesse.getLastName(); //'sanford'
Another quick tidbit about inheritance:
//Guess what jesse.foo will be :)Jesse = function(){};Jesse.foo = "parentobj";Jesse.prototype.foo = "prototype";jesse = new Jesse();jesse.foo; //? (scroll down)
//yeah it's 'prototype'!
Here were some of the good articles that lead to my solution:
http://www.academa.si/?content=http://www.academa.si/html/articles/js/professionalJavaScript/prototipesAndScopeChains.htmhttp://stackoverflow.com/questions/61552/are-there-legitimate-uses-for-javascripts-with-statementhttps://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Function/callhttps://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/function/applymore interesting stuff:
https://developer.mozilla.org/en/JavaScript/Reference/Functions_and_function_scope/argumentshttp://unspecified.wordpress.com/2011/06/05/simulating-classes-with-prototypes-in-javascript/