javascriptoopjavascript-frameworkjavascript-objectsjavascript-namespaces

How to set up JavaScript namespace and classes properly?


It seems there are so many ways to set up a JavaScript application so it is confusing as to which one is correct or best. Are there any difference to the below techniques or a better way of doing this?

MyNamespace.MyClass = {
    someProperty: 5,
    anotherProperty: false,

    init: function () {
        //do initialization
    },

    someFunction: function () {
        //do something
    }
};

$(function () {
    MyNamespace.MyClass.init();
});

Another way:

MyNamespace.MyClass = (function () {
    var someProperty = 5;
    var anotherProperty = false;

    var init = function () {
        //do something
    };

    var someFunction = function () {
        //do something
    };

    return {
        someProperty: someProperty
        anotherProperty: anotherProperty
        init: init
        someFunction: someFunction
    };
}());

MyNamespace.MyClass.init();

The first technique feels more like a class. I am coming from server-side background if this makes a difference. The second technique seems more redundant and a bit awkward, but I see this used a lot too. Can someone please help shed some light and advise the best way to move forward? I want to create a application with lots of classes talking to each other.


Solution

  • Time has passed. I think the right answer now is to use modules (jsdoc typing in the sample).

    Edge.mjs

    /**
     * @typedef {import('./Vertex.mjs').Vertex} Vertex
     */
    
    /**
     * An Edge of a @see Graph
     */
    class Edge {
        /**
         * Create an edge.
         * @param {number} weight - The weight of the edge.
         * @param {Vertex} source - The source vertex of the edge.
         * @param {Vertex} target - The target vertex of the edge.
         */
        constructor(weight, source, target) {
            this.weight = weight
            this.source = source
            this.target = target
        }
    }
    
    export { Edge }
    

    Graph.mjs

    /**
     * @typedef {import('./Edge.mjs').Edge} Edge
     * @typedef {import('./Vertex.mjs').Vertex} Vertex
     */
    
    /**
     * A Graph of @see Vertex and @see Edge
     */
    class Graph {
        /**
         * Creates a new Graph instance.
         * @param {boolean} [isDirected=true] - Is the graph directed (true)?.
         * @param {boolean} [acyclic=true] - Is the graph acyclic (true) or can it contain cycles (false).
         */
        constructor(isDirected = true, acyclic = true) {
            /** @type {Vertex[]} */
            this.vertices = []
            /** @type {Edge[]} */
            this.edges = []
            this.isDirected = isDirected
            this.acyclic = acyclic
        }
    ...
    export { Graph }
    

    index.js

    import { Edge } from './Edge.mjs'
    import { Vertex } from './Vertex.mjs'
    import { Graph } from './Graph.mjs'
    
    export { Edge, Vertex, Graph }
    

    somefile.js

    import { Edge as Edge1, Vertex, Graph } from './index.mjs'
    import { Edge as Edge2 } from './someotherEdge.mjs'
    
        let edge = new Edge1()
        let otherEdge = new Edge2()
    ...
    

    In somefile.js you avoid the name collision yet can use them in the same file. And you avoid trying to make namespaces out of Objects or Functions and whatnot and trying to deal with the jsdoccery that ensues.

    Alternatively, if you are using typescript you are already enjoying the namespace support and all the typing.