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

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

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

 

什么是模块?php

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

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

那么该如何作呢?程序员

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

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

 

命名空间编程语言

单独一个对象名称惟一性保证是很是困难的,在javasript中能够有两个办法来避免这种冲突。ide

目录方式:将同名对象放到不一样目录,好比放到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核心语法基本介绍完毕。

相关文章
相关标签/搜索