Typical Noise

Alex Arnell

Coffee fueled terminal junky. A Software Developer with a strong passion for good software design and an unquenchable thirst for learning.

Showing all posts tagged "Javascript"

Updated OO Library

It has been a while since I last posted and I just wanted to make a quick announcement. Version 2.2 of my Ruby style OO library is now ready for public consumption. It has been sitting around in my darks repository for a good little while now. This new version is functionally identical to the version I last blogged about. The only change I have made is a slight alteration based on some feedback I received from Justin Palmer of EncyteMedia.

In this new version I renamed Class.create to Class.extend, but before you go off on a frantic search and replace. Don't worry, because even though the Class.create method is now deprecated, it won’t disappear for a little while.

So what prompted me to make this change?

Well, Justin pointed a subtle difference between my syntax and the syntax used by Dean Edward's Base.js library.

From Dean Edward's library:

var Square = Shape.extend({ ...
as apposed to my library.

var Square = Class.create(Shape, { ...

In Justin's opinion the syntax of the first version is much more concise than the syntax found in my version. I concur, however in order to achieve that kind of syntax Shape has to have the method extend injected into it's definition by the OO library. This breaks one of the additional set of rules I had defined for my own library. In an effort to make the syntax of my own library a bit more concise I moved the core functionality into Class.extend as seen here.

var Square = Class.extend(Shape, { ...

It's still not nearly as concise, but I feel it is a good compromise. At least the verb extend makes more sense than create in this particular use.

One finally word of warning for those intending to mix my library with Prototype. Even though Class.extend is now the preferred method for creating and extending your own Classes, you are still required to include my library after Prototype in your own work.

Once again head on over to the project page to grab the javascript file.

Javascript OO Ruby Style

This is a follow up post regarding the OO inheritance javascript library I blogged about earlier. I planned on submitting my original work as a patch to Prototype. Before submitting my patch at the rails development site I checked out the competition. I found a couple OO implementations already in contention for the next major version of Prototype. For the curious, both of them are mentioned on Sam Stephenson's weblog.

I spent some time looking over both solutions. Each one tried and did a very good job of implementing OO features, but they both had parts I didn't like. One author thought it would be neat to add support for private methods. This is neat, but I don't think javascript really needs this. People have been writing javascript for years without it and every attempt I've seen to add it to the language has seemed kind of hackish, well at least to me. You'll find my biggest beef with both libraries when you inspect an instance of one of your classes. You'll find a couple added methods and properties that the library injects so that it can do it's job. In fact even my previous OO library was guilty of this, something that bugged me to no end.

So I sat down one evening and rewrote the core of my own library to meet all my demands and expectations. I am proud to say that the new library is much smaller, sleeker and generally just feels better than the first. I also added some neat new features along the way.

Visit the project page for details on how to get the library.

Just like Dean Edwards, I want an OO library that solves a number of problems.
For convenience, here is Dean's list.

http://dean.edwards.name/weblog/2006/03/

  • I want to easily create classes without theMyClass.prototype cruft
  • I want method overriding with intuitive access to the overridden method (like Java’s super)
  • I want to avoid calling a class’ constructor function during the prototyping phase
  • I want to easily add static (class) properties and methods
  • I want to achieve the above without resorting to global functions to build prototype chains
  • I want to achieve the above without affectingObject.prototype

However, I have a few additions.

  • I want a more Ruby like feel to creating and working with Objects.
  • I want to achieve all of the above without adding additional baggage to final object.
  • I want to achieve all of the above while maintaining backward compatibility with Prototype.

So what's so new and great with my OO library? Well, since creating the first version I've learned a few neat tricks to help achieve my goals. First lets talk about mixins. Previously you had to define your mixins on a separate line
after your class definition.

Debug = {
  log: function(msg) {
    alert(msg);
  }
};

Employee = Class.create({
  initialize: function() {
    this.log("In the Employee constructor!");
  }
});
Class.mixin(Employee.prototype, Debug);

Client = Class.create({
  initialize: function() {
    this.log("In the Client constructor!");
  }
});
Class.mixin(Client.prototype, Debug);

Well I've added a bit of syntax sugar that in turn adds more Ruby flavor. It’s now possible to statically define an objects mixins within the class definition.

Comparable = {
  // define methods...
};
Debug = {
  // define methods...
};
Employee = Class.create({
  // define a single mixin
  include: Debug,
  initialize: function() {
    // ...
  }
});
Client = Class.create({
  // define an array of mixins
  include: [Debug, Comparable],
  initialize: function() {
    // ...
  }
});

Each element given to the include property will have all of it's methods mixed into the objects' s definition. All standard rules for Ruby mixins are followed and apply when added this way. Also, it's worth noting that the include property will be removed from the object's definition. This way you won't see it when you inspect instances of your class.

The second improvement is in the this.parent() method. It's now smart enough to recognize what method it is being used inside of, so you no longer have to explicitly tell it what method to call.

Manager = Class.create(Employee, {
  initialize: function(name, dept, title) {
    this.parent(name, dept);
    this.title = title;
  },
  doWork: function() {
    this.parent();
  }
});

If you have defined a property or method using the same name, it will overwritten. It's also important to note that the 'this.parent()' method is only available inside a method definition. Doing something like the following will result in a javascript error.

var joe = new Manager();
joe.parent();

The last visible difference between this version and my previous is that I no longer force properties into the generated objects. This is best demonstrated with an example.

Debug = {
  log: function(msg) {
    document.writeln(msg);
  }
}

Employee = Class.create({
  initialize: function(name, dept) {
    <a href="http://this.name">this.name</a> = name;
    this.dept = dept;
  },
  doWork: function() {
    return <a href="http://this.name">this.name</a> + " is now working...";
  }
});

Manager = Class.create(Employee, {
  include: Debug,
  initialize: function(name, dept, title) {
    this.parent(name, dept);
    this.title = title;
  },
  doWork: function() {
    this.log(this.parent() + "at managing the employees.");
  }
});

var joe = new Manager("Joe", "Sales", "Sales Manager");
joe.doWork()

var out = "";
for (var p in joe) {
  out += p + ": " + joe[p] + ",\\n"
}
out = out.substring(0, out.length - 2);
Debug.log("Manager {\\n" + out + "\\n}");

Outputs:

Joe is now working...at managing the employees.
Manager {
  name: Joe,
  dept: Sales,
  title: Sales Manager,
  initialize: function (name, dept, title) {
    this.parent(name, dept);
    this.title = title;
  },
  doWork: function () {
    this.log(this.parent() + "at managing the employees.");
  },
  log: function (msg) {
    document.writeln(msg);
  }
}

As you can see the for (var p in joe) construct returns exactly what you defined. There are no extra methods or properties visibly added to you object to make any of this work. You can also see that the log method was mixed into the class definition and the include property no longer exists.

Visit the project page for details on how to get the library.