犀牛書學習筆記(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核心語法基本介紹完畢。

相關文章
相關標籤/搜索