简体   繁体   中英

Javascript module Pattern with Namespacing

I have been practicing with JavaScript module pattern with proper Name-spacing. So basically I declare namespaces and each namespace has certain modules encapsulated in it. Here is what I have written so far. The code has been commented properly.

// namespace has been defined somewhere not to worry that it will be undefined
if (NAMESPACE == null || typeof (NAMESPACE) == 'undefined') {
    NAMESPACE = {};

    var id = function (id) {
        var _all_ids = {};
        var persona = {};
        var _id = id; // _id is a local variable that stores the argument

        var getId = function () { //this function returns the local private variable
            return _id;
        }

        persona.getId = getId;
        var _closed = false;

        var close = function () {
            delete _all_ids[getId()];
            this._closed = true;
        }

        persona.close = close;

        return persona; // persona is an object that has two properties `getId` and `close`. Both these are functiona
    }

    NAMESPACE['id'] = id; // so basically this will become NAMESPACE.id.getId or NAMESPACE.id.close
} 

I have fully commented tis code for anyone to understand. It declares a simple namespace and then adds a module in it. The module of course is using encapsulation.

One of my instructors suggest that this code has a basic flaw standard or otherwise. I can't figure this out although the code runs fine.

Is it good enough standard wise? Or am I doing it totally wrong?

I've expanded the example a little bit to show the principles.

// NAMESPACE with install/define method, like when using require.js
var NAMESPACE = (function ( doc, win ) {
    var _modules = {};
    return {
        'install' : function ( name, definition ) {
            if (!_modules.hasOwnProperty(name)) {
                _modules[name] = definition;
                this[name] = _modules[name];
            }
            else throw new Error('Module ' + name + ' is already installed.');
        }
    };
}( document, window ));
// Your actual module to use in the NAMESPACE
NAMESPACE.install('id', (function ( id ) {
    var _all_ids = {}, 
        _id = id, 
        _closed = false; 
    return { 
        'getID' : function () { 
            return _id; 
        }, 
        'close' : function () { 
            delete _all_ids[_id];
            _closed = true; 
        } 
    }; 
}( 'someDefaultID' )));

// NAMESPACE as an object we pass around
var NAMESPACE = {};
(function ( NAMESPACE ) {
    var id,
        NAMESPACE = NAMESPACE;
    if (NAMESPACE == null || typeof (NAMESPACE) == 'undefined') {
        NAMESPACE = {};
        window.NAMESPACE = NAMESPACE;
    }
    id = (function ( id ) {
        var _all_ids = {}, 
            _id = id, 
            _closed = false; 
        return { 
            'getID' : function () { 
                return _id; 
            }, 
            'close' : function () { 
                delete _all_ids[_id];
                _closed = true; 
            } 
        }; 
    }( 'someDefaultID' ));
    NAMESPACE['id'] = id;
}( window.NAMESPACE ))

Why not put the functions inside the namespace?

NAMESPACE = {
 id: function (id) {
  // The id function in here
 }
}

The point of namespacing is to avoid polluting the global scope. In your code,

if (NAMESPACE == null || typeof (NAMESPACE) == 'undefined') {
    NAMESPACE = {};

    var id = function (id) {
        // ...
    }

    NAMESPACE['id'] = id; // so basically this will become NAMESPACE.id.getId or NAMESPACE.id.close
}

id is now defined in the global scope as well as on your namespace. This defeats the purpose of using namespaces.

A simple plain JS way of achieving the same objective of defining your namespace without polluting global scope is by rearranging

if (NAMESPACE == null || typeof (NAMESPACE) == 'undefined') {
    NAMESPACE = {};

    NAMESPACE['id'] = function (id) {
        // ...
    }
}

or

if (NAMESPACE == null || typeof (NAMESPACE) == 'undefined') {
    NAMESPACE = {
        id: function (id) {
        // ...
    }

}

as @Lekoaf suggested.

However, the modern way of avoiding the global pollution is to simply enclose all your code inside an IIFE. While there's there's more than one way to enclose your code in an IIFE, the simplest way is to just drop it in as is

(function(){ // Beginning of IIFE
if (NAMESPACE == null || typeof (NAMESPACE) == 'undefined') {
    NAMESPACE = {};

    var id = function (id) {
        var _all_ids = {};
        var persona = {};
        var _id = id; // _id is a local variable that stores the argument

        var getId = function () { //this function returns the local private variable
            return _id;
        }

        persona.getId = getId;
        var _closed = false;

        var close = function () {
            delete _all_ids[getId()];
            this._closed = true;
        }

        persona.close = close;

        return persona; // persona is an object that has two properties `getId` and `close`. Both these are functiona
    }

    NAMESPACE['id'] = id; // so basically this will become NAMESPACE.id.getId or NAMESPACE.id.close
} 
})(); // End of IIFE

You can use an augmenting module pattern in order to avoid overriding your global module and its methods.

//fooModule does not exist, so it is defined as an empty object
var fooModule = (function(app, name, definition) {
    if (!app[name]) app[name] = definition;
    else throw new Error("this method has already been defined");
    return app;
})(fooModule || {}, "foobar", function() {
    console.log("foobar");
});

//fooModule now exists, so we use the pre-existing object instead of an empty one
var fooModule = (function(app, name, defintion) {
    if (!app[name]) app[namepace] = definition;
    else throw new Error("this method has already been defined");
    return app;
})(fooModule || {}, "barfoo", function() {
    console.log("barfoo");
});

//throws an error because barfoo has already been set to fooModule
var fooModule = (function(app, name, defintion) {
    if (!app[name]) app[namepace] = definition;
    else throw new Error("this method has already been defined");
    return app;
})(fooModule || {}, "barfoo", function() {
    console.log("should throw error");
});

fooModule.foobar(); //"foobar"
fooModule.barfoo(); //"barfoo"    

The important part is myModule || {} myModule || {} . If the module exists, use it, if it doesn't, create the module.

This only addresses methods on a global object. Have you read this article ? Probably the best I've read on module patterns.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM