Archive for the ‘intellisense’ 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!

VS 2010 Intellisense and (function(){}()) wrapper

VS 2010 intellisense will not expose any constructors, properties or methods if the entire module is wrapped in (function(){}());

Define namespaces, and objects in the namespaces, out in the open. The only global will be the namespace definition, and all objects defined in them will be in that scope and not the global namespace scope.

This does not pollute the global namespace.

Consider:

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

JsTest.Objects.Rectangle = function (w, h) {
    "use strict";
    this.width = w;
    this.height = h;
};

JsTest.Objects.Rectangle.prototype.area = function () {
    "use strict"; return this.width * this.height;
};
JsTest.Objects.Rectangle.prototype.circumference = function () {
    "use strict"; return ((2 * this.width) + (2 * this.height));
};

This code sample defines an object called Rectangle in the namespace JsTest.Objects.

Only JsTest is visible in the global namespace.

To make use of our Rectangle object, we call it as such:


<script src="JsTest.js"></script>

<script type="text/javascript">
    var rect1 = new JsTest.Objects.Rectangle(3, 5);</p>
</script>

VS 2010 Intellisense will pickup on our loaded module’s object and it’s properties and methods.

Within a function wrapper, Intellisense works fine. It observes the scope of the variables. It will display them if they are in scope.

Being aware of this will help with coding when the definitions are in one module, and the use of them in another file that includes the module.