Chinaunix首页 | 论坛 | 博客
  • 博客访问: 484274
  • 博文数量: 55
  • 博客积分: 1867
  • 博客等级: 上尉
  • 技术积分: 587
  • 用 户 组: 普通用户
  • 注册时间: 2006-12-29 01:33
文章分类

全部博文(55)

文章存档

2013年(1)

2012年(2)

2011年(16)

2010年(11)

2009年(5)

2008年(10)

2007年(8)

2006年(2)

分类:

2010-11-27 22:56:10

  当构建大型的JavaScript(尤其是那些有几十甚至几百个JavaScript文件)应用程序时,
使用类似Java的包结构来管理JavaScript是非常合适和有意义的,而且Java的包结构为在
JavaScript中引入名称空间准备了很好的模型。如果配合动态加载技术可以写出既优雅又很好
维护的代码,而不用担心或忘记引入一大推不知何用的JavaScript的文件。
  比如,在一个大型项目中,所有的JavaScript文件(第三方的除外)可以这样组织:
  ../js                    ; JavaScript的文件目录
  ../js/com/
  ../js/com/jinfonet/      
  ../js/com/jinfonet/util/ ; 一些工具类
  ../js/com/jinfonet/...   ; 其它
使用这种方式来组织管理JavaScript文件,Java程序员大都会觉得理所当然,不过这也意味
着在使用这些JavaScript文件时,引入这些文件也是一件枯燥和容易忘记的工作,比如要维护
一大堆下面这种语句,



当增加新的JavaScript文件,或者减少一些,又或者重构很多JavaScript文件,维护这样的
东西是常令人沮丧的。
  最近,看了jQuery动态加载JavaScript的实现,和一些在JavaScript里支持名称空间
的实现,启发自己实现了一个让Java程序员更习惯的JavaScript对象文件的组织管理机制。
  这个实现目标是:
  1) 一个类,一个文件。
  2) 在每一个类里,定义自己的包名,并引入自己需要的其它类。实现类似在Java中定义
     一个类的语法:

      package com.jinfonet.util;

      import java.util.List;

      public class MyClass {
          // ...
      }
  3) 除了个别用于初始化的,或者程序入口的JavaScript文件,其它相关的JavaScript
     按需动态加载。
  4) JavaScript文件按Java方式存储在包名目录下。


首先,创建基础的Clazz(../js/com/jinfonet/clazz.js)对象,这个Clazz属于名称空间window.com.jinfonet。见如下代码:

/**
 * File: clazz.js
 * Create: 2010-11-17
 * Author: hoodng@hotmail.com
 */

com = window.com || {};
com.jinfonet = window.com.jinfonet || {};

com.jinfonet.Clazz = (function(){
return {
    PATH_PREFIX : "../js",

    /**
     * Create a Namespace with specified package name
     *
     * @param packageName
     * the package name shoule be like "com.jinfonet.util"
     */

    namespace : function (packageName){
    var names = packageName.split(".");
    var parent = window;
    for(var i = 0, len = names.length; i < len; i++){
     var name = names[i];
     if(parent[name] === undefined){
        parent[name] = {};
     }
     parent = parent[name];
    }
    },
    /**
     * Return whether the specified class was defined
     *
     * @param clazzName
     * The class name should be like "com.jinfonet.util.StringBuffer"
     */

    defined : function(clazzName){
    var names = clazzName.split(".");
    var parent = window;
    for ( var i = 0, len = names.length; i < len; i++) {
     var name = names[i];
     if (parent[name] === undefined){
        return false;
     }
     parent = parent[name];
    }
    return parent !== undefined;
    },
    /**
     * Imports a class dnamically
     *
     * @param className The class name that will import.
     */

    imports : function(clazzName){
    if (j$.defined(clazzName)) return;

    var buf = new Array();
    buf.push(j$.PATH_PREFIX);
    var names = clazzName.toLowerCase().split(".");
    for ( var i = 0, len = names.length; i < len; i++) {
     buf.push("/");
     buf.push(names[i]);
    }
    buf.push(".js");
    var url = buf.join("");

    var http = window.ActiveXObject ?
     new ActiveXObject("Msxml2.XmlHttp") : new XMLHttpRequest();
    http.open("GET", url, false);
    http.send(null);
    
    if (http.readyState == 4 &&
     (http.status == 200 || http.status == 304)) {
    
     var script = document.createElement("script");
     script.type = "text/javascript";
     script.text = http.responseText;
    
     var head = document.getElementsByTagName("head")[0] ||
        document.documentElement;
     head.insertBefore(script, head.firstChild);
     head.removeChild(script);
     script = null;
    }
    }
};// End return

})();

j$ = window.com.jinfonet.Clazz;


以上是Clazz的源代码,有三个方法,
namespace(packageName) // 定义一个名称空间
defined(className)     // 测试一个对象是否已经存在,用来标志一个JavaScript是否已经加载
imports(className)     // 用来从文件加载一个JavaScript的对象

为了方便使用,定义短名称j$指向window.com.jinfonet.Clazz。通常,我们只需要用到以下方法

1) 定义名称空间
   j$.namespace("com.jinfonet.utils");

2) 引入类
   j$.imports("com.jinfonet.utils.StringBuffer");

到此,已经具备定义名称空间,并动态导入JavaScript的能力。比如,我们现在要创建一个com.jinfonet.utils.StringBuffer类,那么,我们需要在路径../js/com/jinfonet/utils/下创建JavaScript文件stringbuffer.js, 代码如下:

/**
 * File: stringbuffer.js
 * Create: 2010-11-17
 * Author: hoodng@hotmail.com
 */

//定义名称空间,是不是很像Java的package com.jinfonet.utils;

j$.namespace("com.jinfonet.utils");


com.jinfonet.utils.StringBuffer = function StringBuffer() {
    this.__buf__ = new Array();
};

with(com.jinfonet.utils){

    StringBuffer.prototype.append = function(str){
    this.__buf__.push(str);
    return this;
    };
    
    StringBuffer.prototype.toString = function(){
    return this.__buf__.join('');
    };
};


以上是我们使用名称空间创建了第一个工具类StringBuffer,还没有看出来优雅在何处。我们再定义另外一个工具类,com.jinfonet.utils.Logger,用于在一个弹出的窗口中显示一些message,可以替代alert。
同样,这个Logger应该对应物理文件../js/com/jinfonet/utils/logger.js,代码如下:

/**
 * File: logger.js
 * Create: 2010-11-17
 * Author: hoodng@hotmail.com
 */

//定义名称空间,是不是很像Java的package com.jinfonet.utils;
j$.namespace("com.jinfonet.utils");


//不优雅吗,这个类用到StringBuffer所以在这里导入,这是很自然的,

//是不是很像Java的import com.jinfonet.utils.StringBuffer;

j$.imports("com.jinfonet.utils.StringBuffer");

com.jinfonet.utils.Logger = (function (){

    var __log__ = undefined;
   

    j$.initLog = function(){
        com.jinfonet.utils.Logger.init();    
    };

    j$.log = function(msg){
        com.jinfonet.utils.Logger.log(msg);
    };
    
    return {
    
    init : function(){
     var win = window.open("", "LogWindow",
        "height=400, width=200, top=0, left=0, ntoolbar=no, menubar=no, " +
        "scrollbars=yes, resizable=yes, location=no, status=no");
    
     var doc = win.document;
     doc.close();
     doc.open();
     doc.write("Log window...");
     doc.write("


         + "style='width:100%; height:100%; white-space:nowrap;'>");
     doc.write("
");

     this.__log__ = doc.getElementById("__log__");

     window.alert = function(msg){
        j$.log(msg);
     };
    },
    
    log : function(msg){
     var now = (new Date()).toString();
     var buf = new com.jinfonet.utils.StringBuffer();
     if(this.__log__){
        buf.append("");
        buf.append("
").append(now).append("
"
);
        buf.append("
");
        buf.append(msg).append("
"
);

        this.__log__.innerHTML += buf.toString();
     }else{
        buf.append(now).append(":\n").append(msg);
        alert(buf.toString());
     }
     buf = null;
    }
    }; // End return
})();


到这里,我们还没有开始真正的应用程序,所以还没有体现出动态加载的优雅,我们接着再来,写一个简单的应用程序,代码如下:

<html>
<head>
  <script type="text/javascript" src="../js/com/jinfonet/clazz.js"></script>
  <script type="text/javascript" src="../js/com/jinfonet/utils/logger.js"></script>

  <script type="text/javascript">

  j$.initLog();

  var i = 0;
  var n = 10;
  
  while(1){
    //j$.log(""+i);
    alert(""+i);
    i++;
    if (i >= n ) break ;
  }

  j$.log("end");

  </script>
</head>

<body>
JavaScript test
</body>

</html>


注意看,在这个应用中,我们没有显示的导入StringBuffer,但StringBuffer在导入logger.js时已经被动态加载了。

  理论上,我们只需要显示导入clazz.js和主应用入口的JavaScript文件,其它的都是动态加载的,由每一个JavaScript文件的作者确定她需要导入哪些类,这样就避免了先前说的,在大型工程中要维护一大堆不知所谓的JavaScript导入。

阅读(2349) | 评论(1) | 转发(0) |
给主人留下些什么吧!~~

chinaunix网友2010-12-01 10:07:20

有点类似 google.load("jquery", "1.4.4"); 但没的它强大 ^^ 一般 js 都伴随着相应的 CSS,是否需要 import("XXX.css")?