Sergio and the sigil

JavaScript, 5 ways to call a function

Posted by Sergio on 2009-02-09
This post is part of a series called JavaScript Demystified.

Time after time I find JavaScript code that has bugs caused by lack of proper understanding of how functions work in JavaScript (a lot of that code has been written by me, by the way.) JavaScript has functional programming characteristics, and that can get in our way until we decide to face and learn it.

For starters, let's examine five ways to invoke a function. On the surface we might be tempted to think that functions work exactly like C#, but we will see that there are important differences and ignoring them will undoubtedly result in hard to track bugs.

Let's first create a simple function that we will be using through the rest of this post. This function will just return an array with the current value of this and the two supplied arguments.

<script type="text/javascript">
function makeArray(arg1, arg2){
	return [ this, arg1, arg2 ];
}
</script>

Most common way, unfortunately, global function calls

When we are learning JavaScript we learn how to define functions using the syntax used in the example above. We learn that it's also very easy to call that function — all we need to do is:

makeArray('one', 'two');
// => [ window, 'one', 'two' ]

Wait a minute. What's that window object doing there? Why is it the value of this? If you haven't stopped to think about it, please stay with me here.

In JavaScript, and I'm not talking specifically about the browser here, there's a default/global object. It's as if every code that we write which seems to be just "loose" inside your script (i.e. outside of any object declaration) is actually being written in the context of that global object. In our case, that makeArray function isn't just a loose "global" function, it's a method of the global object. Bringing ourselves back to the browser, the global object is mapped to the window object in this environment. Let's prove that.

alert( typeof window.methodThatDoesntExist );
// => undefined
alert( typeof window.makeArray);
// => function

What all this means is that calling makeArray like we did before is the same as calling as follows.

window.makeArray('one', 'two');
// => [ window, 'one', 'two' ]

I say it's unfortunate that this is the most common way because it leads us to declare our functions globally by default. And we all know that global members are not exactly the best practice in software programming. This is especially true in JavaScript. Avoid globals in JavaScript, you won't regret it.

JavaScript function invocation rule #1 In a function called directly without an explicit owner object, like myFunction(), causes the value of this to be the default object (window in the browser).

Method call

Let's now create a small object and use the makeArray function as one of its methods. We will declare the object using the literal notation. Let's also call this method.

//creating the object
var arrayMaker = {
	someProperty: 'some value here',
	make: makeArray
};

//invoke the make() method
arrayMaker.make('one', 'two');
// => [ arrayMaker, 'one', 'two' ]
// alternative syntax, using square brackets
arrayMaker['make']('one', 'two');
// => [ arrayMaker, 'one', 'two' ]

See the difference here? The value of this became the object itself. You may be wondering why isn't it still window since that's how the original function had been defined. Well, that's just the way functions are passed around in JavaScript. Function is a standard data type in JavaScript, an object indeed; you can pass them around and copy them. It's as if the entire function with argument list and body was copied and assigned to make in arrayMaker. It's just like defining arrayMaker like this:

var arrayMaker = {
	someProperty: 'some value here',
	make: function (arg1, arg2) {
		return [ this, arg1, arg2 ];
	}
};
JavaScript function invocation rule #2 In a function called using the method invocation syntax, like obj.myFunction() or obj['myFunction'](), causes the value of this to be obj.

This is a major source of bugs in event handling code. Look at these examples.

<input type="button" value="Button 1" id="btn1"  />
<input type="button" value="Button 2" id="btn2"  />
<input type="button" value="Button 3" id="btn3"  onclick="buttonClicked();"/>

<script type="text/javascript">
function buttonClicked(){
	var text = (this === window) ? 'window' : this.id;
	alert( text );
}
var button1 = document.getElementById('btn1');
var button2 = document.getElementById('btn2');

button1.onclick = buttonClicked;
button2.onclick = function(){   buttonClicked();   };
</script>

Clicking the first button will display "btn1" because it's a method invocation and this will be assigned the owner object (the button input element.) Clicking the second button will display "window" because buttonClicked is being called directly (i.e. not like obj.buttonClicked().) This is the same thing that happens when we assign the event handler directly in the element's tag, as we have done for the third button. Clicking the third button does the same of the second button.

That's another advantage of using a library like jQuery. When defining event handlers in jQuery, the library will take care of overriding the value of this and make sure it contains a reference to the element that was the source of the event.

//using jQuery
$('#btn1').click( function() {
	alert( this.id ); // jQuery ensures 'this' will be the button
});

How does jQuery override the value of this? Keep reading.

Two more: apply() and call()

The more you leverage functions in JavaScript, the more you find yourself passing functions around and needing to invoke them in different contexts. Just like jQuery does in the event handler functions, you'll often need to override the value of this. Remember I told you functions are objects in JavaScript? Functions have predefined methods, two of them are apply() and call(). We can use them to do precisely that kind of overriding.

var gasGuzzler = { year: 2008, model: 'Dodge Bailout' };
makeArray.apply( gasGuzzler, [ 'one', 'two' ] );
// => [ gasGuzzler, 'one' , 'two' ]
makeArray.call( gasGuzzler,  'one', 'two' );
// => [ gasGuzzler, 'one' , 'two' ]

The two methods are similar. The first parameter will override this. They differ on the subsequent arguments. Function.apply() takes an array of values that will be passed as arguments to the function and Function.call() takes the same arguments separately. In practice I believe you'll find that apply() is more convenient in most cases.

JavaScript function invocation rule #3 If we want to override the value of this without copying the function to another object, we can use myFunction.apply( obj ) or myFunction.call( obj ).

Constructors

I won't delve into the details of defining types in JavaScript but at minimum we should be aware that there aren't classes in JavaScript and that any custom type needs a constructor function. It's also a good idea to define the methods of your type using the prototype object, which is a property of the constructor function. Let's create a small type.

//declaring the constructor
function ArrayMaker(arg1, arg2) {
	this.someProperty = 'whatever';
	this.theArray = [ this, arg1, arg2 ];
}
// declaring instance methods
ArrayMaker.prototype = {
	someMethod: function () {
		alert( 'someMethod called');
	},
	getArray: function () {
		return this.theArray;
	}
};

var am = new ArrayMaker( 'one', 'two' );
var other = new ArrayMaker( 'first', 'second' );

am.getArray();
// => [ am, 'one' , 'two' ]

What's very important to note here is the presence of the new operator before the function call. Without that your function will just be called like a global function and those properties that we are creating would be created on the global object (window.) And you don't want to do that. Another issue is that, because you typically don't have an explicit return value in your constructor function, you'll end up assigning undefined to some variable if you forget to use new. For these reasons it's a good convention to name your constructor functions starting with an upper case character. This should serve as a reminder to put the new operator before the call.

With that taken care of, the code inside the constructor is very similar to any constructor you probably have written in other languages. The value of this will be the new object that you are trying to initialize.

JavaScript function invocation rule #4 When used as a constructor, like new MyFunction(), the value of this will be a brand new object provided by the JavaScript runtime. If we don't explictly return anything from that function, this will be considered its return value.

It's a wrap

I hope understanding the differences between the invocation styles help you keeping bugs out of your JavaScript code. Some of these bugs can be very tricky do identify and making sure you always know what the value of this will be is a good start to avoiding them in the first place.

Resharper an OutOfMemory Exceptions

Posted by Sergio on 2009-02-03

My current project has an interesting story of being converted across platforms and programming languages. I hope I can talk about this conversion process some day (it was not manual).

One of the side effects of mass conversions like this is that the final result tends to carry over some weird patterns. These patterns used to make total sense in the previous programming language, but look very unnatural in the new one.

We ended up with some very large classes inside a single namespace that, while work exactly as in the previous language, cause an unusual amount of stress inside Visual Studio, especially with Resharper (version 4.1) installed. Resharper would halt with OutOfMemory exceptions all the time, until I disabled Code Analysis, arguably one of the most important features of that product.

Finally today a coworker pointed me to a fix for that. I'm still getting the exceptions if I turn on solution-wide error analysis, but I think I can live without that.

I haven't had a chance to try the nightly builds of Resharper 4.5. I have been told that those issues may go away.

UPDATE: I installed version 4.5 (build 1169.15) but it didn't help me. The only thing that changed was the layout of the error screen :) But I know my project is a tad extreme and I deserve to be punished for it.

Generated by a tool, not for human consumption

Posted by Sergio on 2009-01-30

A few years ago I had an interesting discussion with some of my then coworkers about the XML comments in our code. XML comments were useful in some cases because we were writing some libraries to be shared with many of our applications. We kept the DLLs, the XML and the CHM all in the build folder for any other developer that needed to use that library.

I know some of you have strong opinions against or in favor of XML comments. What I know is that they don't bother me but I'd trade them for a clear and self-explanatory API in a heartbeat.

But what really bothered me was when one of the guys came to me and showed this amazing Visual Studio add-on that would automatically generate the XML comments for him. I won't name the tool here because it's not important. GhostDoc (ooopsy!!!) goes through all your code, finds where XML comments can be put, and tries to guess the text of the comment from the name of the members, parameters, etc. When it finishes, a method named GetOrders will have a description "Gets the orders", a property or parameter named UserName will become "Gets the name of the user", and so on. See image below as an example.

Now, let's think about this for a second. Suppose you are trying to use a class that has a method called GetOrders, do you really need a stupid tooltip comment or a topic in a CHM file to tell you that this method "gets orders" ? Maybe you thought it would list the "orders of the gets", right? Then you bring up Intellisense suggestions and there's a property named UserName in your object, I'm sure you'd be totally puzzled wondering what it stands for, correct?

Hmmm, UserName, what could it possibly be. Ha! Thank you Mr. Tooltip you just saved my bacon. It's the name of the user. Phew, thank God I didn't need to use Reflector to figure this one out.

Sarcasm aside, what real benefit does such a tool gives you? You're just avoiding compiler warnings at most. If you want real useful content in your XML comments, they need to hand-written by someone who's thinking about the developer that will read them. A tool will not add examples, notes, tips, suggest other references, etc. Basically, if a freaking tool was able to guess what that member does, you must be able to guess it too before the tooltip comes up. The tool was not written to help the other developer. The tool was written to beat another tool (the compiler and its annoying warnings.) Use wisdom when choosing your tools. Not all of them are made equal.

What drove me over the edge to get this post out was seeing a tooltip like "Gets the name of the nick".

It takes a master to lead an apprentice

Posted by Sergio on 2009-01-21

Last Monday I once again attended the Software Craftsmanship Group meeting where a panel was held on the topic of Apprenticeship. I was expecting to see a discussion about what constitutes apprenticeship and the master-apprentice relationship. Instead, the conversation was mostly around two examples of companies that have apprenticeship programs.

Dave Hoover from Obtiva and Paul Pagel from 8th Light contrasted their approaches. Uncle Bob posted his impressions about the two programs and his own experiences of carrying an apprentice program, so I won't bother you with a similar (but less qualified) report.

I did bring back a few conclusions and questions, though:

  • It's hard to convince someone to take a job as an apprentice when higher-paying (or better titled) jobs are available.
  • Someone that is willing to take such position will inevitably be passionate for the craft and hungry for knowledge. This eventually helps weeding out the inadequate candidates.
  • Mentors need to keep up with the apprentices. Often they'll outgrow your ability to keep them busy and interested.
  • It takes a lot of energy and investment from the company and mentor to properly guide an apprentice into the path to journeyman. When that apprentice leaves the company, I'm afraid the loss is too big and maybe there wasn't enough time to recoup that investment.
  • Both cases presented had not experienced a failure yet, which is good, I guess. But it also leaves me wondering if it really works. Failures can assist validating the successes, the trick is always tweaking the process to keep the failure rate low (but zero is always suspicious.)
  • It seems too easy to have an apprenticeship program deteriorate into just internship — with a fancy name.

I'll post an update with a link to the video of the meeting if that becomes available.

My Apprenticeship Process

Interestingly enough, I had a chance to join an apprenticeship program a long time ago, before I got into software development.

Between high school and college I started a 5-month internship as an Electronics Technician at a local TV station. The official title was intern but it was pretty obvious there was a lot more going on.

By that time I was in my teens, and very passionate about Electronics, probably just as much as I am now about software. At the TV station I had the honor of working with a true master of that profession. My mentor not only knew the ins and outs of professional audio and video equipment, but he also loved to talk about that and teach his hard-earned techniques to us. He was as highly regarded as any professional can be. Still, his purpose in life seemed to be that every technician was as good or better than him.

Even though I learned more in those 5 months than in my 5 years in college, after seeing my mentor at work I could never dare to say I was anything more than an apprentice. The impression that I carry with me to this day is that apprenticeship is a long process. Even if you get promoted after a while, it's hard to say you're no longer an apprentice. The lines between apprentice, journeyman, and master are indeed blurry.

Unfortunately, in my software career I have never had a chance to go through similar process. Instead, like most of us, I was simply thrown in the fire and fought my way out of it. I learned a lot in these situations, but I'm sure I also wasted a lot of time doing the wrong things until I figured out the right ones. A good mentor would have guided me to the right options without depriving me of the discovery and accomplishment sensations.

Hooray for masters and apprentices! Both with tiny egos and great passion. Both seeking continuous improvement.

Uncle Bob talks Agile at Chicago ALT.NET

Posted by Sergio on 2009-01-19

After an unfortunate cancelation of our last meeting because of the weather, February's meeting of the Chicago ALT.NET Group should be a very interesting one.

Robert "Uncle Bob" Martin looks into XP's rearviewmirror, lessons learned, and the current status of Agile development.

XP: After 10 years, why are we still talking about it?

6:00 pm
Pizza and networking time

6:30 pm

It has been 10 years since Extreme Programming broke upon our industry. In that time we have seen the rise of the Agile movement, and the gold rush for Scrum certification. We have seen the concept of testing do a complete reversal in emphasis; and shift away from heavy planning. But what have we learned? Do we really all program in pairs? Do we really all write our tests first? Do Agile projects really fare better than others? Do we have enough data now to separate the truth from the myth? And why, after all this time, does it still dominate our conferences and conversations. Isn't there something new to talk about?

Robert C. Martin has been a software professional since 1970. In the last 35 years, he has worked in various capacities on literally hundreds of software projects. He has authored "landmark" books on Agile Programming, Extreme Programming, UML, Object-Oriented Programming, and C++ Programming. He has published dozens of articles in various trade journals. Today, He is one of the software industry's leading authorities on Agile software development and is a regular speaker at international conferences and trade shows. He is a former editor of the C++ Report and currently writes a monthly Craftsman column for Software Development magazine.

Mr. Martin is the founder, CEO, and president of Object Mentor Incorporated. Object Mentor is a sister company to Object Mentor International. Like OMI, Object Mentor is comprised of highly experienced software professionals who provide process improvement consulting, object-oriented software design consulting , training, and development services to major corporations around the world.

7:45 pm

Let's use the discussion time to go over the group topics that were planned for last meeting, which had to be canceled.

  • What worked well and what didn't in 2008
  • What direction should we take with our group
  • Viability of our group organizing a CodeCamp soon
  • Global ALT.NET participation: as suggested in this message

If you want to help define our meetings format and group actions, then come and help us in this discussion.

UPDATE: The videos of the event are available.