zoukankan      html  css  js  c++  java
  • 犀牛书学习笔记(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核心语法基本介绍完毕。

  • 相关阅读:
    今天开始用 VSU 2010
    Visual Studio 2010 模型设计工具 基本应用
    Asp.Net访问Oracle 数据库 执行SQL语句和调用存储过程
    Enterprise Library 4.1 Security Block 快速使用图文笔记
    解决“System.Data.OracleClient 需要 Oracle 客户端软件 8.1.7 或更高版本。”(图)
    一个Oracle存储过程示例
    Enterprise Library 4.1 Application Settings 快速使用图文笔记
    Oracle 10g for Windows 简体中文版的安装过程
    Oracle 11g for Windows 简体中文版的安装过程
    Oracle 9i 数据库 创建数据库 Net 配置 创建表 SQL查询 创建存储过程 (图)
  • 原文地址:https://www.cnblogs.com/laohoo/p/3431367.html
Copyright © 2011-2022 走看看