// It is important to declare your variables. (function() { var foo = 'Hello, world!'; print(foo); //=> Hello, world! })(); // Because if you don't, the become global variables. (function() { foo = 'Hello, world!'; print(foo) //=> Hello, world! })(); print(foo) //=> Hello, world! // When global variables sneak into your code the can cause problems. // Especially in applications with concurrency. var count = function() { for (i = 0; i < 10; i += 1) { print(i); } }; count(); //=> 0 1 2 3 4 5 6 7 8 9 var countSilently = function() { for (i = 0; i < 10; i += 1) { // don't print anything; } }; // Both loops increment i at the same time, which causes strange behavior. window.setTimeout(countSilently, 10); window.setTimeout(count, 10); //=> 2 3 7 8 9 // You can use 'this' in method definitions to refer to attributes of the // method's object. var obj = { name: 'foo', introduce: function() { print(this.name); } }; obj.introduce(); //=> foo // But 'this' does not follow the normal rules of scope in JavaScript. One // might expect 'this' to be available with the same value via closure in the // callback defined inside the method here. var obj = { name: 'foo', introduce: function() { window.setTimeout(function() { print(this.name); }, 3000); } }; obj.introduce(); //=> *pause* undefined // In fact, this got bound to the global object in the callback. To get around // this, assign the object reference to a regular variable that will have the // same value inside the callback definition. var obj = { name: 'foo', introduce: function() { var that = this; window.setTimeout(function() { print(that.name); }, 3000); } }; obj.introduce(); //=> *pause* foo // The keyword 'this' is actually dynamically assigned whenever a function is // invoked. When a function is invoked as a method, i.e. obj.method(), 'this' // is bound to 'obj'. But when a function is invoked by itself 'this' is bound // to the global object. var phrase = 'Hello, world!'; var printPhrase() { print(this.phrase); } printPhrase(); //=> Hello, world! // This is true even of functions that were defined as a method. var obj = { name: 'foo', introduce: function() { print(this.name); } }; // When the function is invoked without 'obj.' in front of it, 'this' becomes // the global namespace. var introduce = obj.introduce; introduce(); //=> undefined // Method invocation and function invocation are two of the invocation patterns // in JavaScript. A third is apply invocation, which gives us control over what // 'this' will be assigned to during function execution. introduce.apply(obj, null); //=> foo // 'apply' is a method on Function. The first argument is the value that 'this' // will be bound to. Successive arguments to apply are passed as arguments to // the function that is being invoked. var chatty = function(repeatTimes) { var i; for (i = 0; i < repeatTimes; i += 1) { print(this.name + ' '); } } chatty.apply(obj, 3) //=> foo foo foo // The fourth and final invocation pattern in JavaScript is constructor // invocation. This pattern was designed to provide a way to create new objects // that would appear familiar to programmers who are used to programming with // classes. var Cat = function(name) { this.name = name; }; Cat.prototype = { query: function() { print(this.name + ' says, "meow"'); } }; // When a function is called with the 'new' keyword in front of it, a new // object is created and is bound to 'this' when the function runs. Special // constructor functions use this feature to customize new objects as they are // created. var whiskers = new Cat('whiskers'); whiskers.query(); //=> whiskers says "meow" // When a new object is created with 'new', the prototype of the new object is // set to the prototype of the constructor function. So the new object inherits // all of the attributes of the constructor's prototype value. In this case, // new cat objects inherit the 'query' method from Cat.prototype. var nibbler = new Cat('nibbler'); nibbler.query(); //=> nibbler says "meow" // If a constructor function is called without the 'new' keyword, it is invoked // with the ordinary function invocation pattern. var gotcha = Cat('gotcha!'); gotcha.query(); //=> typein:165: TypeError: gotcha has no properties // So 'this' is assigned to the global object instead of to a newly created object. That means that any attributes assigned to the new object by the constructor function become global variables! print(name); //=> gotcha! // Constructor invocation is pretty complicated and prone to disastrous global // variable creation. Here is a cleaner way to create new objects that inherit // from other objects. // This defines Object.create, a method that simplifies the behavior of the // 'new' keyword. This method was invented by Douglas Crockford. // http://javascript.crockford.com/prototypal.html if (typeof Object.create !== 'function') { Object.create = function(o) { var F = function() {}; F.prototype = o; return new F(); }; } // Object.create(obj) returns a new object that inherits all of the attributes // of obj. The 'cat' prototype object here defines a 'clone' method that wraps // around Object.create to customize new 'cat' objects as they are created. var cat = { query: function() { print(this.name + ' says "meow"'); }, clone: function(name) { var newCat = Object.create(this); newCat.name = name; return newCat; } }; var fluffy = cat.clone('fluffy'); fluffy.query(); //=> fluffy says "meow" // In addition to inheriting 'query', new cats also inherit 'clone'. var fluffy2 = fluffy.clone('fluffy2'); fluffy2.query(); //=> fluffy2 says "meow" // Methods and attributes are inherited, not copied. If you change the // definition of 'clone' on 'cat' at this point, the change will be reflected // in cat objects that have already been created. fluffy2.hasOwnProperty('clone') //=> false fluffy.hasOwnProperty('clone') //=> false cat.hasOwnProperty('clone') //=> true