A life in the day

1/4/2011

No Classes in Javascript

Filed under: — Scott Sauyet @ 9:39 pm

A colleague has published a series of posts (parts 1, 2, 3, 4, and 5) detailing the evolution of a Javascript class. We discussed this at our recent JS Club meeting. I had several concerns there; some I was able to raise and discuss, others I was not. Here I have the leisure to explain my objections more completely. But before I dive into it, I want to point out that while Paul and I disagree on a fair bit, I have a great deal of respect for him. He’s one of the people I most enjoy working with.

My first problem is the very notion of calling something a “class” when discussing Javascript. Javascript is a class-free language. The closest things Javascript has to the object-oriented notion of classes are constructor functions. But they are really not that close. Trying to impose OO thinking on what more closely resembles a functional language seems to start out in the wrong direction. But this may be simply a matter of terminology. We certainly want the ability to dynamically create multiple objects with similar interfaces and to encapsulate their data. If that’s all that’s meant by “classes”, then perhaps we are not too far apart.

The interface that he is creating can perhaps best be described by a few unit tests. Here are my own (QUnit) unit tests showing a partial set of the capabilities of Paul’s “Guy” constructor:

1
 
test("Simple, index-based storage", function() {
    var bob = new Guy("Bob");
    bob.keep({socks: 2});
    equals(bob.show("0"), "{\"socks\":2}", "Expected Bob's socks to be in slot 1");
    bob.keep({looseChange: 6});
    equals(bob.show("1"), "{\"looseChange\":6}", "Expected Bob's loose change to be in slot 2");
    ok(bob.have("socks"), "Bob should still have his socks");
    ok(bob.have("looseChange"), "Bob should still have his loose change");
});
 
test("Named storage location", function() {
    var fred = new Guy("Fred");
    fred.keep({creditCards: 4}, "wallet");
    equals(fred.show("wallet"), "{\"creditCards\":4}", "Expected Fred's credit cards to be in his wallet");
    fred.keep({cash: 50}, "wallet");
    equals(fred.show("wallet"), "{\"creditCards\":4}", "Nothing else can go in Fred's wallet");
    ok(fred.have("creditCards"), "Bob should still have his credit cards");
});

A live version of these tests is at:

http://scott.sauyet.com/Javascript/Test/Guy/2011-01-04a/

Essentially, a Guy has a name and three public functions. He can store various objects, either in indexed locations or in specifically named ones. He can show JSON representations of the objects he’s stored when supplied their name. And he can report whether or not he’s stored something for a given name. These are represented by the functions keep, show, and have. This is a bit oversimplified, but it’s enough for these discussions. The unit tests above are by no means a complete test suite for the constructor function, but they do show much of the expected usage of Guys.

Here is Paul’s final implementation:

1
 
var Person = function (name, basics) {
    this.name = name;
    this.stuff = basics || {};
    this.index = -1;
};
 
Person.prototype.find = function (where, keep) {
    var label = where.split("."),
    box = this.stuff;
    while (label.length > 1) {
        if (typeof box[label[0]] === "undefined") {
            if (keep) {
                box[label[0]] = {};
            } else {
                return;
            }
        } else {
            box = box[label.shift()];
        }
    }
    return [box, label[0]];
};
 
Person.prototype.keep = function (what, where) {
    var found, box, label;
    if (where) {
        found = this.find(where, true);
    } else {
        found = [this.stuff, this.index += 1];
    }
    box = found[0],
    label = found[1];
    if (typeof box[label] === "undefined") {
        box[label] = what;
    }
};
 
var Guy = function (name, basics) {
    var person = new Person(name, basics);
    this.have = function (what) {
        if (typeof person.find(what) !== "undefined") {
            return true;
        } else {
            return false;
        }
    };
    this.show = function (what) {
        var found, box, label;
        if (typeof what === "undefined") {
            found = [person, "stuff"];
        } else {
            found = person.find(what);
        }
        box = found[0];
        label = found[1];
        if (typeof box[label] === "object") {
            return JSON.stringify(box[label]);
        } else {
            return box[label];
        }
    };
    this.please = function (action) {
        var request = Array.prototype.slice.apply(arguments);
        request.shift();
        if (typeof action === "function") {
            action.apply(person, request);
        } else {
            if (person[action]) {
                person[action].apply(person, request);
            }
        }
    };
};
 
Guy.prototype.keep = function (what, where) {
    this.please("keep", what, where);
};

In this implementation, a Guy is created with a hidden reference to a Person. His own-properties, such as have and show, as well as his helper function, please, have access to this Person, but his prototype functions cannot directly access this Person; they need to elevate their privileges by calling please.

The first concern, and the one I raised in our discussion, but couldn’t fully articulate, was my objection to the expanded API this implementation entails. The unit tests above suggest a fairly simple API: There needs to be a constructor function which accepts a name. (The name is not actually used anywhere, but it’s easy enough to imagine there would be a use for it soon.) And the objects created from this constructor should have as properties the functions keep, show, and have. That’s all that’s really required. But this implementation exposes the Guy constructor, the prototype for that Constructor containing a keep function, the Person constructor, and its prototype containing find and keep. The objects created from the Guy constructor have the expected keep, show, and have properties, but they also have a please property exposed. If all those additional API capabilities were meant to be public, I would have no objection. But they are implementation details, which to my mind should be kept private.

A lesser concern has to do with the additional parameter which can be passed into the constructor. This was to allow us to start each Guy off with a certain set of stuff; it’s not tested in the code above. The problem is that, for all the concern about encapsulation in the discussions about the five steps, the implementation uses that object directly if supplied to store its stuff. So code that kept a reference to the object passed to this constructor would have a handle on what’s supposed to be entirely private stuff. This would be a major concern except that if this were actually in a larger system, there would probably some utility clone function which could be readily employed to fix this problem. This one can be chalked up to it being demo code.

The next objection is more substantial, but it might be the hardest to overcome if there is a need for a Guy’s configuration to remain safely encapsulated: Every Guy created has its own copy of the hide, show, and please functions. This could be quite memory-intensive if there are a lot of Guys in the system. Moreover, it seems very contrary to the overall direction of trying to make Javascript a little closer to classical OO languages. I’ll discuss my alternative implementation below. In it I don’t solve this issue; I don’t know of a solution to this. But I do reduce the custom functions to very thin wrappers around otherwise shared functions. Certainly it reduces the problem. This should demonstrate what I mean:

1
 
    var bob = new Guy("Bob");
    var fred = new Guy("Fred");
    console.log(fred.keep === bob.keep);     // true
    console.log(fred.show === bob.show);     // false
    console.log(fred.have === bob.have);     // false
    console.log(fred.please === bob.please); // false

But my biggest concern is that even though a major goal in the discussion was to encapsulate private data in a way that made it safe from prying eyes, this implementation does not manage to do so. Paul said that, “To make sure you don’t trick [a Guy] into giving away his stuff, please will return undefined.” But I can still get at it quite easily:

1
 
    var george = new Guy("George");
    george.keep({creditCards: ["Visa", "Amex"]}, "wallet");
    george.please(function() {pickpocket = this.stuff;});
    console.log(pickpocket); // {"wallet":{"creditCards":["Visa","Amex"]}}
    pickpocket.wallet.creditCards = [];
    console.log(george.show("wallet")); // {"creditCards":[]}

The pickpocket was able to get at George’s stuff, and not just a JSON-stringified copy, but a reference to the original. We could similarly get the entire Person, by simply storing this in the variable. This is rather disheartening for George, and demonstrates a real problem — possibly even an intractable one — with trying to hold data out of view in the closure of a constructor but still allowing access to it from prototype methods. Although it’s nice that we had to say “please” to get this data, it seems wrong to make that the only level of protection.


I created an alternate implementation. It solves the first and last issues completely. The public API is just what we would like, and the private data remains private. I didn’t try to solve the non-cloned constructor parameter issue; that’s simple enough to do, and the same sort of code would solve it for both implementations. For the remaining issue, I reduced the memory requirements for each Guy, but individual objects still have their own implementations of the public functions. Those are simple wrappers around common functions, so the cost is lower, but it does not solve this problem entirely. I’d love to see a technique that manages to do this and still keep the small API and properly encapsulate its data.

This solution is less object-oriented than Paul’s. I choose a more functional approach. As best I could, I kept his functions intact. The test page is here:

http://scott.sauyet.com/Javascript/Test/Guy/2011-01-04c/

The code looks like this:

1
 
var Guy = (function() {
    var 
    find = function (where, keep) {
        var label = where.split("."),
        box = this.stuff;
        while (label.length > 1) {
            if (typeof box[label[0]] === "undefined") {
                if (keep) {
                    box[label[0]] = {};
                } else {
                    return;
                }
            } else {
                box = box[label.shift()];
            }
        }
        return [box, label[0]];
    },
    have = function (what) {
        if (typeof find.call(this, what) !== "undefined") {
            return true;
        } else {
            return false;
        }
    },
 
    keep = function (what, where) {
        var found, box, label;
        if (where) {
            found = find.call(this, where, true);
        } else {
            found = [this.stuff, this.index += 1];
        }
        box = found[0],
        label = found[1];
        if (typeof box[label] === "undefined") {
            box[label] = what;
        }
    },
    show = function (what) {
        var found, box, label, result;
        if (typeof what === "undefined") {
            result = this.stuff;
        } else {
            found = find.call(this, what);
            box = found[0];
            label = found[1];
            result = box[label];
        }
        if (typeof result === "object") {
            return JSON.stringify(result);
        } else {
            return result;
        }
    },
    Guy = function(name, basics) {
        if (!(this instanceof Guy)) {
            return new Guy(name, basics);
        }
        var cfg = {stuff: basics || {}, index: -1};
        this.keep = function(what, where) {keep.call(cfg, what, where);};
        this.have = function(what) {return have.call(cfg, what);};
        this.show = function(what) {return show.call(cfg, what);};
    };
    return Guy;
}());

I don’t have very much to say about this implementation. It’s a few lines shorter than the original. The techniques are nothing new. The actual constructor function is wrapped in a closure along with the basic functions to be used. The objects created have thin wrappers calling these functions, supplying the configuration information.

I should note that one of Paul’s concerns was being able to break apart his implementation into pieces that could be maintained separately. This technique would make that more difficult. This does not worry me, because I believe that such constructor functions should be cohesive pieces that are relatively short and easy for a single person to maintain. But if that’s a bigger issue for the reader than the ones above, then s/he is encouraged to look for other approaches.

In any case, I hope this sheds some light on the different concerns that developers might have on to how to build constructor functions in Javascript. A central issue is that we should be using the strengths of the language rather than trying to make it look like some other type of language. Javascript’s flexible semi-functional nature can be very powerful. Trying to graft on a substantial OO gloss can serve to hide the beauty and the power of Javascript.
make

3/31/2007

Overlabel with JQuery

Filed under: — Scott Sauyet @ 2:24 pm

I’ve been playing with JQuery lately, and when I found a need to use the wonderful little accessible compact form script by Mike Brittain, I thought I’d try to duplicate it with JQuery’s simpler syntax. This is my first attempt at anything close to a JQuery plugin. It works for me, as you can see on the test page.

Here’s the code I wrote (Update: There is an updated version in the comments.)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
jQuery.fn.overlabel = function() {
    this.each(function(index) {
        var label = $(this); var field;
        var id = this.htmlFor || label.attr('for');
        if (id && (field = document.getElementById(id))) {
            var control = $(field);
            label.addClass("overlabel-apply");
            if (field.value !== '') {
                label.css("text-indent", "-1000px");
            }
            control.focus(function () {label.css("text-indent", "-1000px");}).blur(function () {
                if (this.value === '') {
                    label.css("text-indent", "0px");
                }
            });
            label.click(function() {
                var label = $(this); var field;
                var id = this.htmlFor || label.attr('for');
                if (id && (field = document.getElementById(id))) {
                    field.focus();
                }
            });
        }
    });
};

And it would be called like this:

1
2
3
$(document).ready(function() {
    $("label.overlabel").overlabel();
});

I’m wondering whether there are some simplifications to this that a more experienced JQuery user could explain, though. I feel as though it’s still too wordy, and that it spends too much time switching between the DOM elements and the JQuery ones.

In any case, if you are interested in this (public domain) plugin, you can grab a zip here, or just go straight to the Javascript source.

3/15/2005

Stylesheets Plugin

Filed under: — site admin @ 12:52 pm

I’ve created my first WordPress plugin. It allows you to add your own stylesheets independent of the chosen theme. You can download it at http://scott.sauyet.com/php/wp-plugins/stylesheets/. Install is the standard, unzip, drop it in the plugins directory, and activate. It adds a panel to the managment page.

The genesis of this plugin was a little itch I had to scratch: I’ve been playing with themes for my blog. Someday I’ll get around to writing my own, but for now I’m working with many of the beautiful options available online. The trouble is that I kept having to patch the stylesheets with a little extra CSS that I was using on my site. I have some health issues that I’m tracking with markup that looks like this:

    I’m at <strong class="good reading">78</strong>, which is in the
    target range of below <strong class="target reading">100</strong>.

which in this blog looks like:

I’m at 78, which is in the target range of below 100.

and of course sometimes there’s a "class='bad reading'" too. :-( To go along with this, I’d been adding a tiny bit of CSS to the stylesheet for each theme I tested. This is tedious and error-prone, and seems to be against the spirit of WordPress.

So when I started to understand the plugin architecture, I created a tiny plugin that added the necessary CSS to the head of the document. Then I switched to an extenal call to the stylesheet. After that, I arranged for the call to the stylesheet to call through the plugin itself. At this point I realized that these techniques could be useful to others. So I’ve generalized it quite a bit, and tried to reasonably bulletproof it.

I think it’s straightforward to use, and self-documenting. It allows you to create as many separate stylesheets as you like, each one including as much CSS as you choose, linked or imported (maybe later I’ll add the option to include the CSS directly in the head), and associated with whatever collection of media you wish. You can also choose to hide any of these stylesheets.

I’m pretty happy with the results, but so far I am the only user. I would love to hear feedback from WordPress users.
(more…)

1/6/2005

Arguing for an upgrade

Filed under: — site admin @ 12:41 pm

My team has a big architectural meeting coming up soon; it will be time to discuss what’s working, what’s not, what our system wants to be when it grows up. It’s a three-day, offsite meeting. We’re flying in team members from Houston and Indianapolis. It’s a big deal. And I’m trying to prepare.

I want to write a document describing some of the benefits of many of the modern software development tools and techniques. But I’m stumped at where to begin. The problem is that there’s far too much to say.

Our current system is an ASP/VB/SQL Server web application. It works only with IE6. Our ASP pages are mixes of presentation, business logic, and data access. Parts of the navigation relies on Javascript. Some of the HTML is generated inside VB DLLs. There are client-specific functions mixed in with generic stuff. There is a very flat directory structure, with no way to distinguish the functionality of various modules.

There are no automated tests, nor any scripts for manual testing. Building the system is a matter of grabbing the code from several different source-code control databases (and all the code that developers have forgotten to check in), moving it to the production machine, then manually trying out anything you’ve been working on, finding the DLLs you forgot you needed to register, trying it again, calling everyone who worked on the system, adding to the database the tables someone forgot to tell you you needed, lathering, rinsing, and repeating until no one notices any further errors.

And of course there is absolutely no documentation.

The initial system was built by a very talented programmer who was quite new to all the technologies used. She did a great job getting things working, but the system has grown in all directions, and there has been little coordination of the efforts.

What I want to propose is a multi-tiered application architecture, using modern development technologies and techniques. My choice would be Java with Hibernate and Spring, although I don’t have much experience with either framework. But I would certainly be willing to live with .NET or a Python framework. I could even see doing all or some of it in PHP. I would be willing to consider full-fledged J2EE, although that scares me a bit.

Out output should be standards-compliant XHTML and CSS. Javascript should be optional, used only to enhance a working page(1).

Everything should be test-driven. No code should be checked in without passing unit tests, and no code should be written without having tests for it to pass. The builds should be automated. There should be automated functional tests.

In short, we should be building a modern, modular, scalable, testable, automated system.

The trouble is in having so much to say that I can’t seem to even begin actually writing my document. I’ve been working around the edges, mostly analysis of the various options for technology. But I need to work on the main part, and I need to do so soon.

Anyone ever seen good sites describing in management’s terms the advantages to modern techniques?


(1) There is one inescapable exception to this. We have a Javascript WYSIWYG editor. We are told we need to maintain a WYSIWYG editor without relying on any plug-in. Nonetheless, everything else should still work with Javascript turned off.

Powered by WordPress