开发者

Namespacing a javascript library, as optional

开发者 https://www.devze.com 2023-02-06 13:30 出处:网络
I\'m about to start building a JS library that will have multiple modules.Let\'s suppose the lib开发者_运维技巧rary is called Library, and two modules will be called One and Two.I\'d like for end user

I'm about to start building a JS library that will have multiple modules. Let's suppose the lib开发者_运维技巧rary is called Library, and two modules will be called One and Two. I'd like for end users to be able to call the library in two different ways:

Library.One.somefunction(params)

or

somefunction(params)

Basically, I want to give the end users the option of including a namespace or not. Is there a good way to do this? Also, is there a good way to do this if I also want to provide a minified version of the library? This library is something that I could end up in Node.js; for now, I'm going to use it myself, but I want to design it in such a way that it's not too hard to turn in to a sharable project in the future.

Any references you can point me to would be great, thanks!


If you're using Node.js you could leverage the CommonJS module system.

math.js (your library)

exports.add = function() {
    for (var i = arguments.length; i--;) {
        sum += arguments[i];
    }
    return sum;
};

program.js (someone using it...)

var MyMath = require('math');
console.log(MyMath.add(1, 2)); // 3

// ... in different ways

var add = require('math').add;
console.log(add(1, 2)); // 3


The basic idea behind making a "namespace" optional is to assign the functions to the global scope, which is the window object:

window.somefunction = Library.One.somefunction;

You can write an include function that works similar to other languages:

var include = function (library, p) {
    if (!p) {
        for (var prop in library) {
            if (library.hasOwnProperty(prop)) {
                window[prop] = library[prop];
            }
        }
    } else {
        window[p] = library[p];
    }
};

Then just do, as required:

include(Library.One);

Or use particular functions only:

include(Library.One, 'somefunction');

Warnings:

  1. Executing the functions without the dot notation (One.somefunction) will cause the this keyword to refer to window rather than Library.One. This isn't a problem if you don't use this at all. If you have data to share to between functions then you can do so using closure scope instead of this:

    var Library = {};
    
    (function () {
        // I'm a closure, I have local scope
        var sharedData = "I'm shared but private to this scope";
    
        Library.One = {};
    
        Library.One.funcOne = function () {
            alert(sharedData);
        };
    
        Library.One.funcTwo = function () {
            sharedData += "!";
        };
    
    }) ();
    
  2. Others have well-advised not to make your methods global. This is because once it is global, it is global for all files, and therefore likely to conflict with other code. What you can do is modify the import function above to create a new object, and assign everything to that object before returning it. Then files that need shortcuts to particular libraries can do:

    (function () {
        var _ = include(Library.One); // This stays within this scope
        _.somefunction();
    })();
    


well, i don't know what you mean by "good way".
First of all, the whole purpose of a namespace is to collect variables that are related and not scatter them all around your public namespace. Personally I wouldn't use such a thing, but you could loop through your namespace's objects and attach them to the window :

for(var i in Namespace)
    if(Namespace.hasOwnProperty(i))
        window[i] = Namespace[i];


You could do this pretty easily, but are you certain you want to make all the methods global properties?

You could implement it like this (very simplified):

(function( window, undefined ) {

    // Your code setting up namespaces
    var Library = {One:{},Two:{}};

       // function for adding library code to both namespaces.
       // Could be modified to accept an Array of functions/names
    function addToLibraryOne( id, fn ) {
        window[id] = Library.One[id] = fn;
    }

       // add a function
    addToLibraryOne( "somefunction", function( params ) {
           // function code
    });

    window.Library = Library;

})( window );

I'd wonder if you really want to pollute the global namespace like this.

At the very least, I'd make the global properties an option, then only have the function add those if that option is selected.


Well, the second one means that you also want the functions and object and whatever in your modules to be in the global scope. Entirely possible of course, but so against best practices as to be somewhat abhorrent.

For the first part, just declare your Library namespace globally:

var Library = {};

and then start populating it with your modules:

Library.One = {};
Library.Two = {};

and then start adding the functionality to those modules.

(function($) {
  var $.froobString = function(s) { .... };
  ...etc...
})(Library.One);

(Here I've done it as a self-executing anonymous function that passes in Library.One as $.)

To convert all that to globals, have a routine like this:

var convertToGlobals = function(module) {
  for (name in module) {
    window[name] = module[name];
  }
};
convertToGlobals(Library.One)

But then again I'd advise against it.


I might be wrong about this (so this might get downvoted, but I want this evaluated), but I think you are setting up a contradiction with the requirements

1) I want to use namespaces
2) I want to be able to access namespace functionality without the namespace.

basically 2 is "I dont want namespaces".

For implementation, you could just define a bunch of functions globally that route into the namespace, but then why have the namespace to begin with?

0

精彩评论

暂无评论...
验证码 换一张
取 消