Webinar wrap-up: Julian on JavaScript III

ctodx
08 March 2011

On Monday, I presented my third webinar in my series on learning JavaScript when you’re a C# developer. The topic: functions. You can watch the webinar here. You can download the slidedeck here. Here’s the unedited JavaScript file containing all the examples.

Southern Guilford (NC) High School Street Signagephoto © 2010 Joe Wolf | more info (via: Wylio)First off, I talked about how you define a function. My recommendation is the function literal way:

var cl = function(obj) {
  console.log(obj);
};

var f = function fib(n) {
  if (n === 0) return 0;
  if (n === 1) return 1;
  return fib(n-1) + fib(n-2);
};

The reason for this is twofold. First, once you start writing JavaScript in earnest, you’ll be writing anonymous function literals all over the place as callbacks, etc. So it’s best to continue that tradition when you declare a function you’ll be calling in code. Second, it reinforces the idea that functions are objects in JavaScript, they’re not statically bound to an object. The first example there is known as an anonymous function literal (the function has no name before it’s assigned to a variable), and the second example is a named function literal, and is great for writing recursive functions. Why? Just imagine this bit of code:

var g = f;
f = undefined;
cl(g(6));

I’m assigning the function object in f to a new variable g. I then set f to undefined (so if the function was calling itself through f, it would crash), and then I print out the result of g(6).

Next on the webinar I talked about the various invocation patterns for calling a function. First up was method invocation. This is the simplest idea to grasp for C# developers: you’ve got some object and you call a method on that object. The this variable inside the function is set to the object you’re calling the method on, just as in a C# instance method. Here’s my printer object example that prints out the number of times it gets called when it prints some other object:

var printer = {
  callCount : 0,
  log : function(obj) {
    console.log("--- count:" + this.callCount++);
    console.log(obj);
    console.log("---");
  }
};

var someObj = {a:42};

printer.log(someObj); // prints 0 as count
printer.log(someObj); // prints 1 as count

Notice something that I didn’t really point out in the webinar: the log method MUST use the this variable to get at the callCount property, otherwise the normal scoping rules for JavaScript come into play (which we discussed later on).

I then showed a little fun bit of code you can’t do in C#: extending the someObj object to have a callCount property and copying over the log function from my printer object. Lo and behold it works.

someObj.callCount = 23;
someObj.log = printer.log;
someObj.log(someObj);// prints 23 as count

The next way of calling a function is using function invocation. Easy in concept – just call the function but not via an object – but the one that causes the most problems. The reason is that the this variable gets set to the Global Object.

var cl2 = function(obj) {
  var isGlobalObject = function(obj) {
    return obj === window;
  };
  
  console.log("Is this the Global Object? " + isGlobalObject(this).toString());
  console.log(obj);
};

I had a brief sidebar on the Global Object, noting that it has no name, but that the browser will add a property called window to it that references it. In other environments, this property may be called something else. The Global Object is where all unowned variables and functions end up, as global public properties. Anyway this code shows that the this variable for the cl2 function is set to window.

The third way of calling a function is through constructor invocation. Several gotchas to watch out for here. First of all you must call the function through the new keyword.

var Person = function(lastName, firstName) {
  this.firstName = firstName;
  this.lastName = lastName;
};

Person.prototype.print = function() {
  var name = this.lastName + ", " + this.firstName;
  cl(name);
};

var me = new Person("Bucknall", "Julian");
me.print();

Second, the new keyword will create a brand new object, and set its constructor to the function and then call it. The this variable will be set to this new object. The final unusual thing about constructors is that the default return value is the new object; you don’t have to return it explicitly.

The big problem about constructor functions is that you sometimes forget to use the new keyword. Constructors are normal functions, there’s nothing in the language that marks them as special.

me = Person("Oops", "Crash");
me.print();

As I showed in the webinar, what happens here is that this in Person refers to the Global Object, and so the function corrupts it by adding a couple of new properties. Since the function has no return statement, undefined is returned instead and stored in me. The next statement throws an error. I showed how to get round this by using this type of code:

var Person = function(lastName, firstName) {
  if (!(this instanceof Person)) {
    return new Person(lastName, firstName);
  }
  
  this.firstName = firstName;
  this.lastName = lastName;
};

So, if the this parameter is not an instance of Person assume that the caller forgot the new and construct a new object properly and return it. If not, just proceed as normal for a constructor.

The final way to call a function is to use the function’s apply or call methods: the “apply” invocation. Yes, a function is an object, so it can have properties and methods. Both apply and call work by calling the function on an explicit object that you supply. Arguments to the function are either passed one-by-one (call) or as an array (apply).

someObj = { callCount : 23 };
otherObj = { foo : 42 };

printer.log.call(someObj, otherObj);
printer.log.apply(someObj, [otherObj]);

So here I declare some object with a property of callCount and another just to log. I then call the printer.log method but force someObj to be the caller object and pass in otherObj as  the object to log, first using call then using apply.

I then talked about scope in JavaScript and stated that it’s by function and not by block. This is perhaps the hardest difference between C# and JavaScript to overcome. Scope is by function. To resolve a variable or a reference JavaScript will look in the current function, and if not found will look in the next most containing function. If not there, move onto the next outer function. And so on, so forth. Eventually it will look in the Global Object. this always just refers to the function itself; it doesn’t go “deeper”.

My example was a rudimentary ticker object, It outputs an asterisk, and then one second later, another.

var ticker = {
  char : "*",
  show : function() {
    cl(this.char);
    setTimeout(function() { cl(this.char); }, 1000);
  }
};

ticker.show();

The first asterisk is output, but the one that should have been output a second later comes out as undefined. The reason is that the this in the timeout’s callback function is the Global Object, not ticker at all. What I did to overcome this problem was to add a local variable in the show method.

var ticker = {
  char : "*",
  show : function() {
    var self = this;
    cl(self.char);
    setTimeout(function() { cl(self.char); }, 1000);
  }
};

The self variable captures the value of this, and now the anonymous timeout function works correctly. Since the function has no local variable called self, the interpreter looks in the next enclosing function, which happens to be show. Bingo, there it is, and the property access now works. I went a little further and modifies ticker to continue outputting asterisks at one second intervals, until its stop property was set to false:

var ticker = {
  char : "*",
  stop : false,
  show : function() {
    var self = this;
    
    var callback = function() {
      cl(self.char);
      if (!self.stop) {
        setTimeout(callback, 1000);
      }
    };

    callback();
  }
};

ticker.show();

Since the binding of this to the Global Object is such a pain in the callback function, I showed how you could write a special bind function that did it for you:

var bind = function(obj, func) {
  return function() {
    return func.apply(obj);
  };
};

This function takes an object and a function that, when called, needs the passed-in object as the this variable. bind returns a function that does the binding by using the “apply” invocation. Here’s the version of ticker that uses this bind function.

var ticker = {
  char : "*",
  stop : false,
  show : function() {
    var callback = bind(this, function() {
      cl(this.char);
      if (!this.stop) {
        setTimeout(callback, 1000);
      }
    });

    callback();
  }
};

Finally I talked about closures, especially since my examples of function scope used them.

Here’s the simple example of creating a counter object from a function that forms a closure around the startValue parameter:

var createCounter = function(startValue) {
  return function() {
    return startValue++;
  };
};

var counter = createCounter(0);
cl(counter()); // 0
cl(counter()); // 1
cl(counter()); // 2

The createCounter function returns a function that, when called, will return the current value of startValue and increment it. The startValue variable however is only defined in the createCounter function and that function terminates early on. The variable continues to exist though in the closure created by the call and the returned function making a reference to it.

I then showed a nice example of how to create private variables in an object through the use of closures. This is an incredibly important pattern to learn: it’s used all over in libraries like jQuery,

var createCircle = function(x, y, radius) {
  return {
    getX : function() {
      return x;
    },
    getY : function() {
      return y;
    },
    getRadius : function() {
      return radius;
    }
  };
}

var circle = createCircle(1, 3, 10);

cl(circle.getX());
cl(circle.getY());
cl(circle.getRadius());

The createCircle function creates a new object that defines a circle on the screen. It has a position (described by the X and Y coordinates) and a radius. Notice though that the returned object does not have any properties: it just defines three “getters” that return these values. The values are stored in the closure formed by calling the original function. The x, y, and radius values act as if they were “private properties” of the object in C# terms, although of course there is no such thing in JavaScript.

All well and good, but what if you wanted to extend that object with another private property only accessible through a getter and setter? You do it through another closure of course. Here’s a way to add a modifiable color property through its own get/set methods:

var addColorProperty = function(obj, color) {
  obj.getColor = function() {
    return color;
  };
  obj.setColor = function(newColor) {
    color = newColor;
  };
};

addColorProperty(circle, "red");
cl(circle.getColor()); // red
circle.setColor("blue");
cl(circle.getColor()); // blue

You call the addColorProperty passing in the object you want to extend and the default color value. It adds a getter and a setter to the object for you, and the closure makes the color variable the private field that is accessed by those methods.

no comments
No Comments

Please login or register to post comments.