A reader comment on my last post brought it to my attention that support for custom events actually already exists in jQuery! I decided to take a look at their solution. You can find an example here (click on “Examples” and scroll to the bottom). It’s pretty cool, but there are a couple things I don’t like about it.
Here’s the code from their sample:
$("p").bind("myCustomEvent", function(e, myName, myValue) {
$(this).text(myName + ", hi there!");
$("span").stop().css("opacity", 1)
.text("myName = " + myName)
.fadeIn(30).fadeOut(1000);
});
$("button").click(function() {
$("p").trigger("myCustomEvent", ["John"]);
});
You might not be totally familiar with jQuery, but the interface is pretty fluent, so I think this will be somewhat straightforward. The most important concept in jQuery is the wrapped set. You can basically pass in a string formatted like a CSS selector to grab a bunch of elements from the DOM. The first statement starts off by grabbing all the “p” elements and binding a custom element with a callback. The event is then raised on the button click in the next line. You can mostly ignore the contents of the callback function.
DISCLAIMER: The following complaints are based strictly on a quick look at this sample and a little nosing around in the jQuery source code.
Here’s what I don’t like. First, the custom event is bound directly to a set of DOM elements, and I can’t really think of any time I’d want to do this. If you’re working with custom events, they’re most likely going to be “application” events. “profileUpdated” or “commentSaved” or “itemDeleted”: those sorts of things. It doesn’t make any sense to me to tie something like this to a set of elements. One has nothing to do with the other. Why not just have a generic callback that selects all the “p” elements itself? For that matter, it seems like the usage they have here is pretty inconsistent; the event is bound directly to the “p” elements, but the callback then selects all the “span” elements. Doesn’t that mean those same operations will be carried out on every span as many times as we have p elements? If the callbacks aren’t tied to sets of elements, this problem goes away. Maybe there’s some other use case I’m not thinking of, but the whole thing seems semantically flawed. I think they probably did this to keep the API consistent with the standard DOM events, but I think they would have been better served doing something else.
My second complaint is a little bit of a guess, but the code in the sample seems to back it up. Look at how the event is raised. We’re allowed to pass in event arguments, but we have to pack them in an array ourselves. This is more of a minor complaint, but I do think it makes the API a little less nice to work with. This is kind of a shock. I think jQuery’s intuitive interface is its biggest strength; everything works pretty much exactly the way you’d expect it to. The main thing that allows this is that most functions have a lot of intuitive overloads, which is probably why they had to make this compromise. If you’re accepting an arbitrary number of arbitrary arguments, there’s really no way to tell what’s what. I can understand the compromise, but I don’t really like it.
To disclaim again, I really like jQuery, though I am a little unfamiliar with this part of the API. Just some uninformed thoughts on the subject.
I’ve been working on a project recently that’s very JavaScript heavy. This probably makes some of you cringe, but I think JavaScript is probably one of my favorite languages. True, it has a lot of terrible quirks and gotchas, but the core of the language is light, powerful, and expressive. For an example of this, I’m going to talk a little bit about a lightweight event framework I built in JavaScript for this project.
When I say “events,” I’m not talking about the DOM. This is obviously a solved problem. I’m talking about events in a more general sense; your application grows to a certain size, and it becomes convenient to pass messages around between components. Along the way, we’ll need to learn about a few (ok, several) of JavaScript’s foibles and a few advanced techniques. Let’s get started:
var CLARITY = {
events: {}
};
Here I’m defining a global object (CLARITY) via object literal syntax. Object literal syntax allows you to define objects inline as a list of key-value pairs. Our object has one property (events), which is itself an empty object literal. This is one of my favorite features of JavaScript, since it gives you a lot of flexibility. You may be wondering why we need to wrap everything in our global object. This is because of a design flaw in JavaScript. It has no concept of namespaces, so everything defined at the top level goes on the global object (window). We can restrict our footprint on the global object by defining our own app-level object to wrap everything. This becomes really important when you start using third party libraries, since you don’t want to clobber someone else’s globals and introduce any subtle bugs.
Let’s flesh out our events object:
var CLARITY = {
events: {
subscribe: function(eventName, callback) { },
publish: function(eventName) { }
}
};
We’ve added two functions to events: subscribe and publish. Note that we’ve done this via anonymous functions, another really powerful feature of JavaScript. In JavaScript, functions are first-class. This means a function can be treated like any other kind of value. This is going to define our base API for our event framework. You can subscribe by passing an event name and a callback function, and you can publish an event with just the event name. Let’s describe our subscribe function:
var CLARITY = {
events: function() {
var events = {};
return {
subscribe: function(eventName, callback) {
events[eventName] = events[eventName] || [];
events[eventName].push(callback);
},
publish: function(eventName) { }
}
} ()
};
This is probably the most complicated single step, so don’t worry if it looks a little alien. First, notice that events is no longer a simple object literal. We now have an anonymous function that returns an object literal. This might seem confusing, but notice that it is invoked immediately. So events is really still being assigned the same object literal. We’ve added this extra piece of indirection to take advantage of closures. We’ve defined an empty object called events right inside the function. In JavaScript, scoping is at the function (rather than block) level. When we return the object literal, any references to objects defined within the function are captured. This means our events object lives on inside our subscribe-publish object, but no one else has direct access to it. This is a really powerful concept, and it gives us a lot of power over how we use objects in JavaScript.
Let’s turn our attention to the subscribe function. Our general strategy is going to be to use events as a hash table mapping event names to arrays of callback functions. This way, we can have multiple callbacks for a given event. We can easily accomplish this, because all objects in JavaScript can be thought of as hash tables. In fact, of these two lines, the second is merely syntactic sugar for the first:
events['something'] = function() { };
events.something = function() { };
So the first line of subscribe is checking to see whether a property with this event name exists on events. If not, it assigns an empty array literal. We do this by using the or operator. If the first value is undefined, it evaluates to false, and we take the second value. The or operator is often called the guard operator when used like this. Now that we know there’s an array in place for us to use, we push our callback onto it. This is pretty much it for our subscribe function.
Publish is even simpler:
var CLARITY = {
events: function() {
var events = {};
return {
subscribe: function(eventName, callback) {
events[eventName] = events[eventName] || [];
events[eventName].push(callback);
},
publish: function(eventName) {
var i, callbacks = events[eventName];
if (callbacks) {
for (i = 0; i < callbacks.length; i++) {
callbacks[i]();
}
}
}
}
} ()
};
We check to make sure the event exists, then iterate over the array and invoke all our callbacks. Fairly straightforward. We can check that this works with the following simple code:
CLARITY.events.subscribe('something', function() {
alert('event received!');
});
CLARITY.events.publish('something');
Which produces the following result:
Sweet. This works for simple cases, but it would be a lot more useful if we could actually pass some payloads around. It’s not immediately clear how to go about this, but there are ways:
var CLARITY = {
events: function() {
var events = {};
return {
subscribe: function(eventName, callback) {
events[eventName] = events[eventName] || [];
events[eventName].push(callback);
},
publish: function(eventName) {
var i, callbacks = events[eventName], args;
if (callbacks) {
args = Array.prototype.slice.call(arguments, 1);
for (i = 0; i < callbacks.length; i++) {
callbacks[i].apply(null, args);
}
}
}
}
} ()
};
Notice the reference to arguments that seems to come out of nowhere. Arguments is actually an implicit local variable in every function. It’s an array-like object that holds each argument passed to the function. This allows for some interesting metaprogramming possibilities. JavaScript doesn’t enforce the defined parameters for function calls, so you can pass as many (or as few) as you’d like. They’ll be bound to the defined parameters in the order they’re passed, until you run out of defined parameters. If you want any other parameters, you’ll need to use arguments.
The phrase “array-like object” may stand out. Because of a strange design decision, arguments is not an array, but an object with properties named 0, 1, 2, etc., and a property named length. The first line inside the if statement is a sort of hack to make an array out of arguments. We can call the slice function (which takes a subset from an existing array) directly from Array’s prototype with the call function. These are equivalent:
var array = [1, 2, 3, 4], slice;
slice = array.slice(2);
slice = Array.prototype.slice.call(array, 2);
When we call slice directly from our array instance, the instance is the context of the function invocation. This means that “this” will be bound to the instance. If we were to call slice directly from the prototype:
slice = Array.prototype.slice(2);
“this” would be bound to the global object. (For some reason . . .) The call function allows us to specify a binding for “this” followed by the normal arguments the function is expecting. Arguments is not an array, but it has enough similar properties that the function behaves correctly and returns a new array. Note that we specify 1 as our starting index, so that args is an array of all arguments passed in other than the event name.
Now that we have an array of payload arguments, we need a way to send it to our callback. We could do this:
callbacks[i](args);
But this is less than ideal. This would require our callback function to treat its expected payload as an array, even if it only wanted one value. Fortunately, we have the apply function.
Apply is a sister function to call. It’s the same insofar as allows you to specify a scope binding for the function, but it handles the passed arguments differently. Whereas call expects the parameters separately, apply takes an array. (Well, actually, it takes an array-like object, but arrays are extremely array-like.) The values in the arguments array are then bound to the defined parameters of the function in question, so you can use them as expected. Take this example:
CLARITY.events.subscribe('something', function(one, two) {
alert('event received with payload: ' + one + ' and ' + two);
});
CLARITY.events.publish('something', 'does this work?', 'you know it!');
This yields the desired result:
Note that the parameters don’t need to be strings; they can be any kind of objects:
CLARITY.events.subscribe('something', function(data) {
alert('event received with payload: ' +
data.one + ' and ' + data.two);
});
CLARITY.events.publish('something', {
one: 1,
two: 'two'
});
With result:
I’m not sure if stuff like this already exists. Searching for JavaScript events obviously gets you a lot of stuff about DOM events. Anyway, I thought this was kind of cool, so hopefully someone can get some ideas from it.
I was reading some old posts on Wil Shipley’s excellent blog the other day, when a subtle (and very common) error caught my eye. I don’t mean to call Wil out by this; I was reading his blog specifically because I think he has great advice about software development. The error in question was talking about some of the problems with the outdated Carbon framework that much of Apple’s older software is built on. The following caught my eye:
Parameters passed into functions are passed by reference, whether they are going to be modified or not, which is just obscure. For example, see SecKeychainItemCopyAttributesAndData(), which takes the input "info" by reference, even though it's read-only!
For those who don’t know, the Carbon framework is written (I believe completely) in C. Can you spot the semantic error? The following quote from K & R will explain:
In preparing for the call to a function, a copy is made of each argument; all argument-passing is strictly by value. A function may change the values of its parameter objects, which are copies of the argument expressions, but these changes cannot affect the values of the arguments. However, it is possible to pass a pointer on the understanding the function may change the value of the object to which the pointer points.
In C, every parameter is passed by value. Now, I’m sure Wil knows the this and simply misspoke, but a lot of people make this mistake, so I’d like to take the time to explain the difference.
All parameters in C are passed “by value.” This means that, when a function is invoked, copies are made of each of its arguments for the function to use freely. When we say a parameter has been passed “by reference,” we mean that the function has received the actual object passed to it and not a copy. It’s easy to mistakenly believe C includes this concept because C++ does; one being a (not quite strict) superset of the other, it takes familiarity with both to know where one stops and the other begins. What Wil is really talking about when he mentions passing parameters “by reference” is passing pointers by value. While the function receives a copy of the pointer passed to it, both still point to the same location in memory. The function can then make changes to the memory pointed to, which the caller will see. This is how shared references between function and caller are done in C. (This also makes the benefits of C++’s added “pass by reference” semantics a little unclear to me.) Let’s explore the differences with a couple concrete examples.
#include <stdio.h>
void chgval(int *ip)
{
printf("*ip in chgval before change: %d\n", *ip);
*ip = 5;
printf("*ip in chgval after change: %d\n", *ip);
}
int main(int argc, char **argv)
{
int i = 3;
int *ip;
ip = &i;
printf("*ip before chgval: %d\n", *ip);
chgval(ip);
printf("*ip after chgval: %d\n", *ip);
return 0;
}
Here we define a pointer to an int (ip) and assign to it the address of an int (i). If you’re not familiar with the semantics of C, just know that, in this example, “ip” refers to the address of an int, whereas “*ip” is the int itself. Inside the chgval function, we assign a new value to *ip. Note that the address it points to is unchanged, so this reassignment can bubble back up to the caller. When we run this program, we see the expected output:
However, if you don’t know that parameters in C are passed by value, you might attempt to make other changes.
#include <stdio.h>
void chgptr(int *ip)
{
printf("*ip in chgptr before change: %d\n", *ip);
int i = 5;
ip = &i;
printf("*ip in chgptr after change: %d\n", *ip);
}
int main(int argc, char **argv)
{
int i = 3;
int *ip;
ip = &i;
printf("*ip before chgptr: %d\n", *ip);
chgptr(ip);
printf("*ip after chgptr: %d\n", *ip);
return 0;
}
This code is similar, but it attempts to reassign the pointer itself in the chgptr function. This works, but only within the scope of the function. Since the parameter is a copy of the original pointer, we see this output:
The original pointer remains untouched.
These examples have been in C, but the concepts are still important to .NET developers. This is because C# has the unrelated concepts of value/reference types and passing parameters by value/reference. The former is used to talk about what happens when we copy some object. Do we copy its value, or do we just copy a reference to it? The latter is what we’ve been discussing in C, except C# does include the concept of passing parameters by reference (using the “ref” keyword). Consider the following code:
static void Main(string[] args)
{
object o = "original";
ChangeObject(o);
Console.WriteLine(o);
}
static void ChangeObject(object o)
{
o = 5;
}
You might know that objects in C# are copied by reference and expect the change inside our function call to persist after it returns. Not so! The parameter is still passed by value. What you really need is this:
static void Main(string[] args)
{
object o = "original";
ChangeObject(ref o);
Console.WriteLine(o);
}
static void ChangeObject(ref object o)
{
o = 5;
}
This specifies that the parameter should be passed by reference.
I’m sure this is common knowledge to a lot of people, but I’ve heard it explained incorrectly enough times that I wanted to clarify.
Hope this helps.