- Subscribe to RSS Feed
- Mark as New
- Mark as Read
- Bookmark
- Subscribe
- Printer Friendly Page
- Report Inappropriate Content
At the beginning of this series I alluded to a mysterious thing called prototype.js that would allegedly make defining JavaScript classes a little easier. We use prototype.js for all the classes we define in ServiceNow's Script Includes.
But what is prototype.js, anyway? In this post we'll delve into it a bit, just for the parts we use in ServiceNow's server-side JavaScript classes. But in general, prototype.js is a popular open source JavaScript library created by Sam Stephenson, and used by thousands of web sites for their client-side JavaScript code. Not only is it a great library for its intended purposes, but it's also a great resource for anyone who wants to learn about JavaScript — the prototype.js is some of the most beautiful JavaScript code I've run across.
Here's what we do with prototype.js on the ServiceNow instance (and my little colleague at right is all ears):
There are two pieces of prototype.js that we use. The first one does a neat little trick: it lets you define the body of the constructor function in your prototype. Here's our example from two posts ago transformed to use prototype.js's class definition style:
var MyObj = Class.create();
MyObj.prototype = {
initialize: function(x, y) {
this.x = x;
this.y = y;
},
x: null,
y: null,
average: function() {
return (this.x + this.y) / 2;
}
};
MyObj.toString = function(){
return 'MyObj';
}
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');
There are really just a couple difference here from what we did without prototype.js.
First, instead of defining a constructor function, we assigned Class.create() to our constructor function property. What does that do? Well, if you'd like to see the code, go look in the PrototypeServer Script Include — but in short, what it does is to create a constructor function that calls the initialize() (spelled the American way!) method. Then if you look at the object literal that we use to initialize our prototype, you'll see that it defines an initialize() method. So all that's going on here is that the Class.create() automatically makes a constructor function that calls initialize(), which we defined to be exactly like our original class' constructor function. What's the point? Well, it lets us define all the instance methods and properties in a single object literal, including the constructor function. It's just a bit more convenient.
The second difference is a bit more subtle. When you run this code, the second of these two lines behaves differently than it did in our original code:
gs.log(MyObj.toString());
gs.log(mo.constructor.toString());
The reason the second form doesn't work now has to do with the details of how Class.create() works. The function that gets assigned to MyObj.prototype.constructor is created by a closure, and isn't the same function as MyObj. There's an easy fix, though, and it works both with and without prototype.js: just define your toString() method like this:
MyObj.toString = function(){
return 'MyObj';
}
MyObj.prototype.constructor.toString = function(){
return MyObj.toString();
}
The second piece of prototype.js that we use in the ServiceNow instance makes it easier to create child classes, or classes that extend other classes. Actually, JavaScript doesn't natively support class inheritance at all; strictly speaking, prototype.js is simulating class inheritance for us. I won't get into detail here about why one would want class inheritance — trust me, it's occasionally very useful. You can see lots of examples of class extension in the Script Includes that come with your ServiceNow instance (for example, the Discovery plugin has a number of them). Here we'll just show you how easy prototype.js makes this. If I wanted to extend the class above, I'd make something like this:
var MyChildObj = Class.create();
MyChildObj.prototype = Object.extendsObject(MyObj, {
mult: function() {
return this.x * this.y;
}
});
var mco = new MyChildObj(5, 12);
gs.log(mco.average());
gs.log(mco.mult());
If you run this code (by appending it to the earlier code), you'll see that in fact a new JavaScript class MyChildObj is created, and that class "inherits" all the properties and methods of the MyObj class. Again, if you'd like to see the code that does this magic, go look in the PrototypeServer Script Include. It's not at all difficult to understand, but you don't actually have to understand it to use it.
Got it?
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.
