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.
photo © 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.
Free DevExpress Products – Get Your Copy Today
The following free DevExpress product offers remain available. Should you have any questions about the free offers below, please submit a ticket via the
DevExpress Support Center at your convenience. We’ll be happy to follow-up.