10 March, 2012

WinJS - Classes

In my last two posts on JavaScript for Windows 8 I looked at scope and namespaces. I pointed out that namespaces could be used to create “static” classes. I hasten to add they can be used for more, don’t make a hard link in your brain between namespaces and static classes.

So what about regular classes. There are a couple of ways to define a class in JavaScript, a simple way is to return a hash from a factory method.

We can shift name into a private scope by declaring a new variable inside our constructor which is captured by the resulting object.

However, for complex classes you intend to create a lot of, this method is generally not the suggested. Each instance is redefining the functions rather than simply pointing to an existing implementation in memory. This is where prototype inheritance comes in.

With this setup you start with your constructor, you then extend it’s prototype. Finally instead of calling the constructor directly, you call it with the new keyword, which creates you a new instance.

Note the capital P in Person, this is a convention to say this function is a class definition, so call it with new.

But, we have lost the private scoping on the name property. To get it back, we need to define the property inside the constructor like we did above, but then expose it with a getter method so that the methods on the prototypes can get at it.

It’s starting to get ugly isn’t it. Getting our reference to Person back out is quite awkward. The getter isn't much fun either.

So what does WinJS bring to the table?

There is a WinJS.Class namespace that contains a define method. It can be used to wrap up a big ugly class definition like that above into a simple method call that returns a class definition you can new up. It supports passing in your constructor, your instance methods/properties and your static methods/properties.

Much cleaner. Our class definition is sitting in the MyApp namespace ready to be created via new MyApp.Person('') and for good measure there is a static factory method MyApp.Person.createPerson('').

But what about private variables? You can use the same technique as my example above with the getter. If you had a lot of them you might wrap them all onto a single private hash that only needed a single getter. What you might see a lot of people do is simply prefix them with an underscore and hope others follow the convention that properties starting with an underscore are not to be touched.

Private scope aside, by combining WinJS.Namespace and WinJS.Class you have a really nice set of helpers to wrap up the task of correctly managing global scope and efficient class definitions. Neither is performing any magic, their source code is there for you to explore, they just get rid of some of the confusing boilerplate.

2 comments:

Aaron Powell said...

You actually have a bug in your last two examples, the '_name' variable is shared across all instances of Person so it will always contain the value of the last Person created.

Because of the way JavaScript scoping works you can't use private variables *and* prototypal inheritance so if you're aim is to have name as a private property you're going to have to create the sayHello method inside the "constructor" as demonstrated here: https://gist.github.com/2033034

Chris Sainty said...

Thanks Aaron,
There were a couple of problems in the post. I tend to evolve my examples as I write articles and occasionally get things mixed up along the way when examples change without reconsidering the article as a whole.

I have rewritten sections of it to hopefully be more correct.