Archive for the ‘closure’ Tag

How to write JavaScript object definitions

Credit goes to Brandon for pointing this out. Intellisense in VS2010 will work. Export the object definitions out from the function wrapper. As I was working on this, it progressed through 3 options. The first two have lead to option 3, which at the moment is my preferred pattern.

(Option 1)

/* global JsTest */
var JsTest = JsTest || {};
JsTest.Objects = JsTest.Objects || {};

JsTest.Objects = (function () {
    "use strict";
    var p = {};

    p.Rectangle = function (width, height) {
        this.width = width;
        this.height = height;
    };

    p.Rectangle.prototype.area = function () {
        return (this.width * this.height);
    };

    p.Rectangle.prototype.circumference = function () {
        return ((2 * this.width) + (2 * this.height));
    };

    p.Square = function (side) {
        this.side = side;
    };

    p.Square.prototype.area = function () {
        return this.side * this.side;
    };

    p.Square.prototype.circumference = function () {
        return 4 * this.side;
    };

    p.Circle = function (radius) {
        this.radius = radius;
    };

    p.Circle.prototype.area = function () {
        return (3.14 * (this.radius * this.radius));
    };

    p.Circle.prototype.circumference = function () {
        return (3.14 * (2 * this.radius));
    };

    return p;
})();

(function () {
    "use strict";

    var rect1, sq1, cir1, msg;

    rect1 = new JsTest.Objects.Rectangle(5, 7);
    msg = "Rectangle(" + rect1.width + ", " + rect1.height + ")\n";
    msg += "Area: " + rect1.area() + "\n";
    msg += "Circumference: " + rect1.circumference() + "\n";
    alert(msg);

    cir1 = new JsTest.Objects.Circle(8);
    msg = "Circle(" + cir1.radius + ")\n";
    msg += "Area: " + cir1.area() + "\n";
    msg += "Circumference: " + cir1.circumference() + "\n";
    alert(msg);

    sq1 = new JsTest.Objects.Square(5);
    msg = "Square(" + sq1.side + ", " + sq1.side + ")\n";
    msg += "Area: " + sq1.area() + "\n";
    msg += "Circumference: " + sq1.circumference() + "\n";
    alert(msg);
})();

(Option 2:)

/* global JsTest */
var JsTest = JsTest || {};
JsTest.Objects = JsTest.Objects || {};

JsTest.Objects.Rectangle = (function () {
    "use strict";
    var p = {};

    p = function (width, height) {
        this.width = width;
        this.height = height;
    };

    p.prototype.area = function () {
        return (this.width * this.height);
    };

    p.prototype.circumference = function () {
        return ((2 * this.width) + (2 * this.height));
    };

    return p;
}());

JsTest.Objects.Square = (function () {
    "use strict";
    var p = {};

    p = function (side) {
        this.side = side;
    };

    p.prototype.area = function () {
        return this.side * this.side;
    };

    p.prototype.circumference = function () {
        return 4 * this.side;
    };

    return p;
}());

JsTest.Objects.Circle = (function () {
    "use strict";
    var p = {};

    p = function (radius) {
        this.radius = radius;
    };

    p.prototype.area = function () {
        return (3.14 * (this.radius * this.radius));
    };

    p.prototype.circumference = function () {
        return (3.14 * (2 * this.radius));
    };

    return p;
}());

(function () {
    "use strict";

    var rect1, sq1, cir1, msg;

    rect1 = new JsTest.Objects.Rectangle(5, 7);
    msg = "Rectangle(" + rect1.width + ", " + rect1.height + ")\n";
    msg += "Area: " + rect1.area() + "\n";
    msg += "Circumference: " + rect1.circumference() + "\n";
    alert(msg);

    cir1 = new JsTest.Objects.Circle(8);
    msg = "Circle(" + cir1.radius + ")\n";
    msg += "Area: " + cir1.area() + "\n";
    msg += "Circumference: " + cir1.circumference() + "\n";
    alert(msg);

    sq1 = new JsTest.Objects.Square(5);
    msg = "Square(" + sq1.side + ", " + sq1.side + ")\n";
    msg += "Area: " + sq1.area() + "\n";
    msg += "Circumference: " + sq1.circumference() + "\n";
    alert(msg);
}());

(Option 3)

/* global JsTest */
var JsTest = JsTest || {};
JsTest.Objects = JsTest.Objects || {};

JsTest.Objects = (function () {
    "use strict";
    var p = {};

    p.Rectangle = (function () {
        var s;

        s = function (width, height) {
            this.width = width;
            this.height = height;
        };

        s.prototype.area = function () {
            return (this.width * this.height);
        };

        s.prototype.circumference = function () {
            return ((2 * this.width) + (2 * this.height));
        };

        return s;
    }());

    p.Square = (function () {
        var s;

        s = function (side) {
            this.side = side;
        };

        s.prototype.area = function () {
            return this.side * this.side;
        };

        s.prototype.circumference = function () {
            return 4 * this.side;
        };

        return s;
    }());

    p.Circle = (function () {
        var s;

        s = function (radius) {
            this.radius = radius;
        };

        s.prototype.area = function () {
            return (3.14 * (this.radius * this.radius));
        };

        s.prototype.circumference = function () {
            return (3.14 * (2 * this.radius));
        };

        return s;
    }());

    return p;
}());

(function () {
    "use strict";

    var rect1, sq1, cir1, msg;

    rect1 = new JsTest.Objects.Rectangle(5, 7);
    msg = "Rectangle(" + rect1.width + ", " + rect1.height + ")\n";
    msg += "Area: " + rect1.area() + "\n";
    msg += "Circumference: " + rect1.circumference() + "\n";
    alert(msg);

    cir1 = new JsTest.Objects.Circle(8);
    msg = "Circle(" + cir1.radius + ")\n";
    msg += "Area: " + cir1.area() + "\n";
    msg += "Circumference: " + cir1.circumference() + "\n";
    alert(msg);

    sq1 = new JsTest.Objects.Square(5);
    msg = "Square(" + sq1.side + ", " + sq1.side + ")\n";
    msg += "Area: " + sq1.area() + "\n";
    msg += "Circumference: " + sq1.circumference() + "\n";
    alert(msg);
}());

Either define all objects in one closure, and export them to JsTest.Objects (warning: this will overwrite and redefine JsTest.Objects!) or, define each JsTest.Objects.Object in a specific closure, or… a combination of both: one wrapper closure containing all the object definitions, each of which is defined in their own closure.

Matter of programming style?

  1. Option 1
    1. It can be difficult just looking at the closure code to separate out the various objects.
    2. It does allow us share private methods defined in the scope of the closure.
  2. Option 2
    1. Somewhat difficult looking at the closure code to decide which object’s code we are looking at. There is a slight effort in looking at the object definition to decide which object definition we are viewing.
    2. Cannot share private methods.
  3. Option 3
    1. Combination of both options 1 & 2. Objects defined in a well contained closures.
    2. Private methods accessible in the outer closure accessible by object definition inner closures.
    3. Easy to determine just by looking at the outer closure which object definition we are examining.

A note on the warning: In the earlier post, I had JsTest.Objects.Rectangle defined without using closures (a.k.a. function wrapper). By saying “JsTest.Objects = function () { …” I effectively overwrote the earlier Rectangle definition!!!  Note to self: don’t mix styles of defining objects. Pick one, and consistently apply it or risk messing things up!

“use strict” in a function?

JsLint started to complain about using the “use strict” in a function form. What???

I found this Douglas Crockford posting from December 2010: “Strict Mode Is Coming To Town

I was code testing delete Array[i]. Here’s a code sample that implements the recommendation, and passes JsLint:


(function () {
"use strict";
var myArray = ["one", "two", "three", "four"];
}());

So… if developing modules, from here on out, in the module file wrap it all up in a closure, and “use strict” will respect the scope of the closure, and not clobber up all the old stuff that is not ES5 ready… (I think it was referred to as “sloppy” co-mingled with “strict”)