- Subscribe to RSS Feed
- Mark as New
- Mark as Read
- Bookmark
- Subscribe
- Printer Friendly Page
- Report Inappropriate Content
This is the first in a short series on JavaScript classes and objects, something I get a lot of questions about. In this series I'll be exploring how the JavaScript class mechanism actually works and how the prototype.js library makes defining classes a bit easier.
A few months ago one of my colleagues made an offhand comment that I think summarizes how a lot of developers understand JavaScript classes and objects. He said: "I understand the mechanics of defining a class, but I think of them as a magic incantation — I don't actually understand what's happening when I use them." Take a deep breath, and let's dive into it:
Here's a silly little sample script that I'm going to use as an example:
var MyObj = function(x, y) {
this.x = x;
this.y = y;
}
MyObj.toString = function(){
return 'MyObj';
}
MyObj.prototype.x = null;
MyObj.prototype.y = null;
MyObj.prototype.average = function() {
return (this.x + this.y) / 2;
}
var mo = new MyObj(5, 12);
gs.log(mo.average());
gs.log(MyObj.toString());
gs.log(mo.constructor.toString());
JSUtil.logObject(mo, 'mo, an instance of MyObj');
JSUtil.logObject(mo.constructor.prototype, 'prototype of MyObj');
I'm going to dissect this, top to bottom. First bit:
var MyObj = function(x, y) {
this.x = x;
this.y = y;
}
This is defining a constructor function for the class named MyObj. There's really nothing special about a constructor function. It's just an ordinary function whose purpose is to initialize a new instance of a class. You'll see a little later how JavaScript knows that's what you're doing. Next bit:
MyObj.toString = function(){
return 'MyObj';
}
This is creating a class method on the constructor function. Take a minute to understand this, if the idea is new to you. The constructor function is a function object — and just like any other JavaScript object, you can define new properties for it. If the property you define happens to itself be a function (like this one), then you can call it just like any other function. When you define a function as a property on a constructor function, we call that a class method because you can call it without having an instance of that class. You'll see this in action a little later. Tip: defining a toString() method on constructor functions is a good general practice, because (as you'll see) it lets other code figure out what kind of object they've got. Next bit:
MyObj.prototype.x = null;
MyObj.prototype.y = null;
MyObj.prototype.average = function() {
return (this.x + this.y) / 2;
}
These three statements define the prototype for my class. Note that the prototype is just another property defined on the constructor function. It's called a prototype because it defines the model for instances of this class — the properties and methods defined in the prototype become the properties and methods that instances of this class have. It's an example of a class property for the same reason the toString() method we just discussed is called a class method. In this prototype, we're defining two instance properties (x and y) and an instance method (average). These are called instance properties and instance methods because you must have an instance of this class to use them. You'll see this in action in a moment. Next bit:
var mo = new MyObj(5, 12);
Here I'm actually creating an instance (in mo) of the class MyObj. Here's where we're telling JavaScript to do something different with the constructor function — and it's all because of the magic word new. By putting that word in there, we're telling JavaScript to do these things (in this order):
- Create a new generic instance of the built-in JavaScript class Object.
- Find the constructor function named MyObj.
- Copy the properties and methods of the constructor function's prototype to the new object. For example, if the prototype has a property named 'x', then it will define a new property named 'x' on our new object, and copy the prototype's value to it.
- Define a property named constructor on our new object, and initialize it with a reference to our constructor function. Many developers don't know this property exists, but it can be quite handy!
- Invoke the constructor function to initialize our new object. When the constructor function is invoked, this contains a reference to our new object. The constructor function, like any other function, may have parameters. In this case we're passing a 5 and a 12 to initialize our instance of this class.
- Return a reference to our shiny new, freshly initialized object (in this case, so that I can assign it to mo).
Whew! That's a lot of stuff for a three-letter keyword. Seems like it ought to be multisyllabic, don't you think? Next bit:
gs.log(mo.average());
gs.log(MyObj.toString());
gs.log(mo.constructor.toString());
Here we're exercising various methods of our class and instance. The first line calls the instance method average() for our instance in mo, and it returns the expected result. Note that we had to call that method by referring to our instance. Next we call the class method toString() by referring to our constructor function directly, by its name. Finally the last line here does exactly the same thing as the preceding line — except that instead of referring to our constructor function by name, we're getting the constructor function indirectly, by referencing the constructor property of our instance (mo.constructor). Consider the implications of that: that means you can write code that can figure out what kind of an object it has (perhaps obtained through a parameter) — but only if you were polite enough to define a toString() method on the constructor function. That's why I said earlier that it was a good idea to do so! Last bit:
JSUtil.logObject(mo, 'mo, an instance of MyObj');
JSUtil.logObject(mo.constructor.prototype, 'prototype of MyObj');
Here I'm just logging what my instance looks like, and what my class' prototype looks like. Here's the result:
Log Object: mo, an instance of MyObj
Object
x: number = 5
y: number = 12
average: function
Log Object: prototype of MyObj
Object
y: null = null
average: function
x: null = null
The first output shows my instance in mo. You can see the instance properties with their expected values 5 and 12, and the instance method shows as a function. All as expected. The second output shows the contents of the class prototype (contained in MyObj.prototype, which is the same thing as mo.constructor.prototype). It has the same two instance properties, but containing no value, and it has the same instance method. Does it make sense that the properties you see in the first output were created by copying the properties you see in the second output? Then does it make sense that after the properties in the instance were created, they were initialized by the constructor function?
Ok, now you're an expert on the basics of how classes and objects in JavaScript work!
- 1,331 Views
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.