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核心语法基本介绍完毕。

  • 相关阅读:
    Html5 冒泡排序演示
    Html5 Json应用
    Html5 和 CSS的简单应用
    Html5 布局方式
    Html5 绘制五星红旗
    Html5 绘制旋转的太极图
    Html5绘制时钟
    Html5 实现灯笼绘制
    "电量信息"组件:<battery> —— 快应用组件库H-UI
    "系统音量"组件:<volume> —— 快应用组件库H-UI
  • 原文地址:https://www.cnblogs.com/laohoo/p/3431367.html
Copyright © 2011-2022 走看看