犀牛书学习笔记(10):模块和命名空间

貌似WEB开发初期,相关脚本语言都是没有考虑到模块化和命名空间的,比如javascript、asp、php等,随着WEB开发规模越来越大,编程语言体系越来越成熟,模块化和命名空间成为必须的语言特性。遗憾的是,javascript目前还没有从语言层面对模块化和命名空间进行支持,但基于语言的灵活性,可以变通的实现。

注:commonJS规范提出了模块化和命名空间规范,NodeJS实现了此规范。

 

什么是模块?

一般来讲,模块是一个独立的JS文件。模块文件可以包含一个类定义、一组相关类、一个实用函数库或者一些待执行的代码。模块化的目标是支持大规模的程序开发,处理分散源中代码的组装,并且能让代码正确运行,哪怕包含了不需要的模块代码,也可以正确执行代码。

理想状态下,所有模块都不应当定义超过一个全局标识。通过把模块定义在某个函数的内部来实现,定义的变量和函数都属于该函数的局部变量,在函数外不可见。实际上,可以将这个函数作用域用做模块的命名空间(模块函数)。而函数都是作为全局对象的一个属性,而javascript代码模块化,所必须遵守的最重要的规则就是:避免定义全局变量。因为,当定义一个全局变量时,都有被其它模块覆盖的危险。

那么该如何做呢?

var ModuleClass = {};  
ModuleClass.函数名1=function(){  
    函数体;//这个函数看起来是一个对象的方法。对,可以利用对象作为一个名字空间  
}   
ModuleClass.函数名2=function(){  
    函数体;  
}  

ModuleClass是一个对象,使用对象作为一个名字空间,将所有的函数及变量都放在其中。即使函数或变量重名( 即不同对象中有相同函数名),它们不在一个名字空间中,这样就不会有被覆盖的危险了。 ModuleClass其实就是在全局变量中定义了一个标示符,为了避免重名,我们应该保证对象名称的唯一性。

 

命名空间

单独一个对象名称唯一性保证是非常困难的,在javasript中可以有两个办法来避免这种冲突。

目录方式:将同名对象放到不同目录,比如放到util目录中“util/ModuleClass”,代码应该这样写:

var util;  
if(!util) util = {};//第一级域名  
util.ModuleClass = {};//第二级域名  
util.ModuleClass.函数名1=function(){  
    函数体;  
}   
util.ModuleClass.函数名2=function(){  
    函数体;  
}  

这是一级目录的形式,如果是多级目录呢,就编程了JAVA包命名方式了。

类JAVA包名方式com.公司名.项目名.util.ModuleCalss,相应的目录路径是:com/公司目录/项目目录/util/ModuelClass.js

var com;  
if(!com) com={};//如果com不存在,则新生成一个  
else if(typeof com!="object"){//如果已存在,但不是一个对象,则抛出一个异常  
   throw new Error("com already exists and is not an object");  
}  
  
if(!com.util) com.util={};//如果com.util不存在则新生成一个  
else if(typeof com.util!="object"){//如果com存在,但不是一个对象,则抛出一个异常  
    throw new Error("com.util already exists and is not an object");  
}  
  
if(!com.util.ModuleClass){//如果com.util.ModuleClass存在,则直接抛出异常  
    throw new Error("com.util.ModuleClass already exists");  
}  
com.util.ModuleClass = {//在com.util.ModuleClass不存在的情况下,我们才能正常使用在此命名空间下定义的代码  
    函数1:function(){ 函数体; },  
    函数2:function(){ 函数体; }  
};  

JAVA中包命名和目录路径的对应关系本身就非常不灵活,JVM类加载机制依赖目录路径来查找类,并加载。在动态语言中,应该像python一样,只需将模块文件放到一个目录,然后在类中引入即可,带来相对路径名称。改变目录,只需要修改类的引入申明代码,而不需要修改模块文件。

从语言角度,这非常的不合理且弱智,期待所有EMCAcript实现都实现CommonJS规范。

犀牛书提供了一个工具类,帮助程序员来创建命名空间。

代码/* == Module and NameSpace tool-func == 
*     author : hongru.chen 
*     date : 2010-12-05
*/

var Module;
//check Module --> make sure 'Module' is not existed
if (!!Module && (typeof Module != 'object' || Module.NAME)) throw new Error("NameSpace 'Module' already Exists!");

Module = {};

Module.NAME = 'Module';
Module.VERSION = 0.1;

Module.EXPORT = ['require', 
                 'importSymbols'];

Module.EXPORT_OK = ['createNamespace', 
                    'isDefined',
                    'modules',
                    'globalNamespace'];
                    
Module.globalNamespace = this;

Module.modules = {'Module': Module}; 

// create namespace --> return a top namespace
Module.createNamespace = function (name, version) {
    if (!name) throw new Error('name required');
    if (name.charAt(0) == '.' || name.charAt(name.length-1) == '.' || name.indexOf('..') != -1) throw new Error('illegal name');
    
    var parts = name.split('.');
    
    var container = Module.globalNamespace;
    for (var i=0; i<parts.length; i++) {
        var part = parts[i];
        if (!container[part]) container[part] = {};
        container = container[part];
    }
    
    var namespace = container;
    if (namespace.NAME) throw new Error('module "'+name+'" is already defined');
    namespace.NAME = name;
    if (version) namespace.VERSION = version;
    
    Module.modules[name] = namespace;
    return namespace;
};
// check name is defined or not 
Module.isDefined = function (name) {
    return name in Module.modules;
};
// check version 
Module.require = function (name, version) {
    if (!(name in Module.modules)) throw new Error('Module '+name+' is not defined');
    if (!version) return;
    
    var n = Module.modules[name];
    if (!n.VERSION || n.VERSION < version) throw new Error('version '+version+' or greater is required');
};
// import module
Module.importSymbols = function (from) {
    if (typeof form == 'string') from = Module.modules[from];
    var to = Module.globalNamespace; //dafault
    var symbols = [];
    var firstsymbol = 1;
    
    if (arguments.length>1 && typeof arguments[1] == 'object' && arguments[1] != null) {
        to = arguments[1];
        firstsymbol = 2;
    }
    
    for (var a=firstsymbol; a<arguments.length; a++) {
        symbols.push(arguments[a]);
    }
    
    if (symbols.length == 0) {
        //default export list
        if (from.EXPORT) {
            for (var i=0; i<from.EXPORT.length; i++) {
                var s = from.EXPORT[i];
                to[s] = from[s];
            }
            return;
        } else if (!from.EXPORT_OK) {
            // EXPORT array && EXPORT_OK array both undefined
            for (var s in from) {
                to[s] = from[s];
                return;
            }
        }
    }
    
    if (symbols.length > 0) {
        var allowed;
        if (from.EXPORT || form.EXPORT_OK) {
            allowed = {};
            if (from.EXPORT) {
                for (var i=0; i<form.EXPORT.length; i++) {
                    allowed[from.EXPORT[i]] = true;
                }
            }
            if (from.EXPORT_OK) {
                for (var i=0; i<form.EXPORT_OK.length; i++) {
                    allowed[form.EXPORT_OK[i]] = true;
                }
            }
        }

    }
    //import the symbols
    for (var i=0; i<symbols.length; i++) {
        var s = symbols[i];
        if (!(s in from)) throw new Error('symbol '+s+' is not defined');
        if (!!allowed && !(s in allowed)) throw new Error(s+' is not public, cannot be imported');
        to[s] = form[s];
    }
}

 使用方式

//为模块创建名称空间
Module.createNamespace("com.davidflannagan.Class");

//填充名称空间
com.davidflanagan.Class.define=function(data){};
com.davidflanagan.Class.provides=functon(o,c){};

//检查模块指定版本是否存在
Module.require("com.davidflanagan.Class",1.0);

//使用模块名称标示符导入模块
Module.importSymbols(Module);
importSymbols(com.davidflanagan.Class);
var Class={};
importSymbols(com.davidflanagan.Class,Clas,"define");

 

至此,javascript核心语法基本介绍完毕。

posted @ 2013-11-19 14:50  LaoHoo  阅读(456)  评论(0编辑  收藏  举报