asfman
android developer
posts - 90,  comments - 213,  trackbacks - 0
JSVM的核心实现

  这一部份内容可以说是JSVM最复杂的地方,主要完成操作有命名空间的导入、类的加载。
  
  命名空间导入函数为系统函数_package,为什么要导入命名空间呢?因为JSVM的命名空间其实是虚拟出来的,比如js.lang.Object,它的命名空间为js.lang,但在JavaScript中是不存在所谓的命名空间,因此其实它是一个对象!js为最高层对象,接下来js有个子对象lang,lang再有一个子对象Object,因此导入命名空间的过程其实是对这些可能不存在的对象的声明过程。

// * 定义包的相关实现 (泣红亭注:我习惯把包package称为命名空间namespace,因为个人习惯,在这里意义是一样的^_^)
function _package(pkname) {
       var pkNames = pkname.split(".");
       var pn = null;
       for(var i = 0; i < pkNames.length; i++) {
              if (i == 0)
                     pn = pkNames[i];
              else
                     pn += "." + pkNames[i];
              if (_typeof(pn) != "function") {
                     var _s = "function " + pn + "(){}";
                     execScript(_s, "JavaScript");
              }
       }
}

  在这个_package函数里使用到另一个系统函数_typeof,_typeof函数其实是对typeof函数的改装,它与typeof不同的是_typeof的参数是一个字符串,而typeof传入的是任何类型的对象。比如可以使用_typeof("window")来得到window的类型,而使用typeof的话必须使用typeof(window)。那两者功能如此相似,为什么还要定义这一个_typeof呢?因为在JSVM中对类的操作很多时候都是把类名用一个字符串当参数的,使用_typeof比较方便。

  回归正题,谈一谈_package的实现方式与思想。

  _package的参数为命名空间字符串,比如要使用js.lang.Object之前必须先_package("js.lang"),注意不是_package("js.lang.Object"),不过你就算是使用_package("js.lang.Object")也是没什么问题,只是提前把Object声明为一个对象,在真正编译它的代码的时候会进行重定义。

  _package做的工作就是把命名空间字符串分解成一个数组,然后循环从上到下把命名空间里的对象声明为一个Function对象,但在声明之前会调用_defind来判断是否已经定义过以免重定义。避免重定义的原因是已经定义过的对象可能又有它特殊的属性与方法,如果重定义会丢失这些东西。
  
  类文件导入函数为系统函数_import,但实际代码并不是在这个函数的定义里边,而是在JSVM.loadPackage,后边会讲到,这里只讲它的使用方法。

  如果你需要使用到一个类,比如js.lang.Object,你必须导入该类的命名空间_package("js.lang"),接下来再使用_import("js.lang.Object"),导入后你就可以使用它的完整类名js.lang.Object或者使用它的短类名Object声明一个该类的对象。短类名是为了方便程式的书写,但这里有一个问题,如果有相同的短类名,那么只有第一个声明的有效。比如存在两个类js.lang.Object以及js.util.Object,这两个类的短类名都为Object,只有import的那个类的短类名有效,后import的类只可以使用完整类名进行声明对象。
  
  JSVM虚拟机对象把JSVM要实现的基本功能以面向对象的方式组织起来,接下来会对它进行简单的讲解。因为代码比较多,讲解这个实在是有些困难,我的表达能力又有限,它大部份工作是自动进行的,实在用到它的时候又不算多,如果有必要的话也只能靠个人仔细研究它的代码。
  
  JSVM对象的成员:

    deviceNumber: JSVM的设备号,现在的值为十六进制数值0x3116,它一般是使用来生成出错代码,不知道还有没有其它用法,我看不出来。
      autoUpdate: 是否自动更新,自动更新指的是不使用lib一次性把整个lib文件放入Cache而是需要到某个类就下载某个类的文件进行编译,如果设置为false则速度会比较慢,而设置为true的话第一次运行会比较慢,因为要一次性把所有的类代码(即lib)下载。
      initialize: 初始化函数
      loadPackage: 把一个包的所有类代码加载进行编译(调用defineClass方法),注意这里与_package是不同的,_package只是预定义所有可能使用到的对象,而它是实实在在把整个命名空间下的类进行编译,进行此操作后你就可以不需要import某个该命名空间下的类。
      loadClass: 加载一个类,并进行编译
      definePackage: 调用_defined导入某个命名空间
      defineClass: 根据传入的类名以及类代码进行编译,同时生成短类名
      unloadClass: 把一个已经定义过但以后又不需要使用的类卸载掉,这样可以节省资源。
      loader: JSVM的核心,实现的功能有加载某个类、加载某个包的所有类、得到某个类的代码、得到某个包的类列表。在这里应用到了Cache,具体思想我在蓝色理想的某置顶贴里有谈到,就不多讲了,直接到那里看就OK。
      run: JSVM为了把同一站点的网页组织成一个整体,它提供了一个框架,具体可以查看cn.x86.framework.Framework以及万常华在JSVM无忧原贴中的说明(同时也有给出应用实例)
      io: 标准输出输入,封将了window.alert以及window.prompt。
      share: JSVM共享模式实现,在JSVM FrameWork框架中为了节省资源,所有的类都指向使用顶级窗口的类,也就是说在顶级窗口加载过的类在子窗口都可以直接使用。

  感觉这一章节讲得很不清楚,虽然我也想把每一行代码分析一下,但我在打字的时候才发觉这个想法是多么难实现,许多东西是“只能意会,不能言传”,我只能做到提供一个基本的概念,真正理解还要靠大家自己翻看代码。
posted on 2006-08-10 16:08 汪杰 阅读(1899) 评论(1)  编辑 收藏 引用 所属分类: javascript

FeedBack:
# re: JSVM的核心实现
2006-08-10 16:09 | 汪杰
一、前言

第一次来到蓝色理想的感觉就是这里的规定好多哦,比如我在置顶贴自报家门说我是无忧脚本论坛的泣红亭就被罚写三篇文章,还特别要求那文章必须没有在无忧脚本发过的,郁闷极了,真有这种规定吗?呵呵。不过入乡随俗,仔细看了一下这里比较热门的话题,发现无忧的两个热门话题在这里都有谈到,一个是幻宇的星际JS版,另一个是万常华的JSVM。

我看大家可能对幻宇的星际更感兴趣一点,但我却挑了JSVM,为什么呢?

首先,星际是还没有到那种程度可以实际应用,而且开发的工作也轮不到我们,它是幻宇个人或者他的Team在独立开发的,而JSVM任何人只要掌握了它的使用方法就可以应用在网站中,甚至你还可以自己动手为它扩充类库!这一点是非常重要的,我觉得这才是真正的代码开放。

其实许多人对JSVM不大感兴趣也是可以理解的,老实说,要理解它的思想是很难的,因为它是属于Web底层的开发,但如果你理解了它的思想,你会发现在JS领域突然出现了一片新的天地,当我看到这片天地的时候,很兴奋,整整看了两个通宵的JSVM代码,然后自己动手编写出一个类似的项目,我称我编写的项目叫做JsPackage,已经正式应用在我的个人主页上了,不过没有拿出来公布,一方面是因为JSVM比我写的好,没有必要拿出来出丑;第二个方面是同类的东西没有必要存在两个,有用的话,一个JSVM就OK了。不过我提倡代码开放,如果哪位网友有兴趣可以跟我说一声,我立刻双手奉上。

附:
1. JSVM在无忧脚本的原贴为 基本上实现 javascript 的 OOP (0423版)
2. 幻宇的星际在无忧脚本的原贴为 【原创】星际争霸js版进度:提供完整源码下载,大家都来down吧!!!

PS: 不是做广告,提供原贴是因为里边有许多非常好的讨论,可以学到许多东西。


二、JSVM是什么?

什么是JSVM?我想由万常华(wch3116)大哥自己来说比较准确,以下是在原贴里他的发言引用:

一直以来,js 没有统一的编写规范,导致许多优秀的代码不能很好的复用(function的复用性很差的)。就拿 51js 来说, 一直在讨论零散的技术,而不能借助众人的力量积累出一系列相对完整的“产品”,这都是因为没有“规范”,不成“体系”造成的。我希望能通过 JSVM 改变这种状态,就如斑竹所言,这只是一套方法(规范),而并完整的产品。众人拾柴火焰高,依照一个统一的规范,每个人根据自己的所长开发自己的类包,就能很好的流传开,被其他人使用。

(万常华的发言引用完)

很明显,JSVM要提供一个平台,或者是一个编码规范,通过类库可以减轻我们写脚本的工作量,同时我们也可以编写自己的类库,这一切在JSVM已经被实现了。


三、JSVM的架构

如果您真的决定继续把文章看下去,请先到JSVM无忧原贴下载最新的JSVM,在第一贴就能够找到,有两份代码,建议全部下载。

首先请解压缩第一份代码,也就是"jsvm & js 核心类库",可以看到有一个文件夹jsre,里边有两个文件夹classes、lib以及一个js文件jsvm.js。所有的类文件都应该放在classes下边,具体如何后边会讲。

这里的jsvm.js里边的代码就是JSVM的核心实现代码,我将会对里边做尽可能详尽的讲解与分析,如果不懂的话可以回贴提问,我会尽可能回复,如果有一些问题比较模糊,最好是直接到原贴里回贴发问,毕竟谁也没JSVM的作者更理解这份代码。

lib文件夹里有两个文件:JsLibTools.hta以及lib-inf.xml。前者是万常华自己提供的一个小工具,使用了HTA技术写的,主要用来把分散的js代码组织成一个xml文件,称它为lib。后者是lib的更新信息。

进入classes文件夹可以看到js文件夹以及class-inf.xml。js文件夹里存放的代码主要是优化JSVM以及为JSVM提供基础类的代码文件,按命名空间的形式存在,比如js.lang.Object这个类的代码就放在/jsre/classes/js/lang/Object.js里边。同时每个文件夹都有package.xml文件,这里边存放了该文件夹里的类信息。

另外万常华还提供了一个cn类库,即第二个代码下载,解压缩之后把cn文件夹放在classes里边。该类库与js类库不同的是它提供了许多实际应用中可以使用的代码,而前者提供了基础类。

JSVM系统变量:

$system_home: 系统目录,默认值为"/common/",这是以网站的绝对路径进行定义的,在使用JSVM之前必须进行修改。
$js_classpath: 类文件存放的根路径
$js_libpath: 类库文件存在放目录,这里的类库即前边所说的"lib"xml文件。
$js_runMode: 运行模式,有两个选项,一个是debug,一个是run。run模式是实际应用的运行模式,程序出了错不提醒访问者,另一个是debug模式,在写代码的时候有时候出了问题,设置为debug可以得到相关的出错信息。

PS: 从上边系统变量的定义可以看出一个特点:系统变量必须使用$开头。


四、JSVM的编码约定

编码约定万常万并没有给出一个比较详细的说法,不过我们可以从他在原贴的发言以及JSVM代码的组织中得出以下规范:

1. 系统变量必须使用$开头
2. 系统函数必须以_开头
3. 继承必须使用类的_extends方法而不允许直接使用prototype = new ClassName的方式
4. 所有的类都必须直接或者间接继承js.lang.Object
5. 包(package)的命名开头字母必须小写
6. 类(class)的命名开头字母必须大写
7. 在类的定义之中使用的临时变量必须以下划线_开头
8. 所有的类都必须严格遵守命名空间的约定(关于命名空间的组织可以查阅Java或者.NET)
9. 例外处理不要使用throw而是使用JSVM自定义的_throw

五、JSVM的例外处理

JavaScript本身就自带有一套例外处理,可以利用try-catch结构处理代码中可能出现的出错,但JSVM觉得这样还不够,因为这里的JavaScript是使用在Web页面中,如果没有使用try-catch的话还是同样会提供各种错误,JSVM是重写window.onerror把这种错误也拉进来一起处理,同时还定义了一个系统函数_throw,实现原理很简单,不需多讲。

具体的原型为window.onerror = function(msg, url, line)

msg为出错信息的描述,即Error对象的description属性
url为出错页面的url
line为出错的行数

在JSVM中,例外处理到处都会用到,这本来是很正常的事情,因为每一个程序员都应该处理所有可能会出现的错误。但因为种种原因,极少人在Web客户端的JavaScript中进行例外处理,大家都觉得没什么必要,从这一点也可以看出万常华编码的严谨。

六、JSVM的继承

在JavaScript中,继承是基于原型的,比如B类要继承A类可以这样子:

A.prototype = new B;

但是在JSVM中并没有如此简单,请看jsvm.js中的这段代码:

// * 定义实现 OOP 的相关方法
Function.prototype._extends = function(jsclass) {
try {
var _cn = (new RegExp("function[\\s]*([\\w|\\.]*)[\\s]*\\(")).exec(this.toString())[1]; // 得到完整类名
if (typeof(jsclass) == "string")
eval("jsclass = " + jsclass + ";");
if (typeof(jsclass) != "function")
_throw(0x000D, "JSVM/_extends:" + _cn + " 继承类错误{" + jsclass + "不是合法类}");
var _p = this.prototype = new jsclass(); // 原型继承
this._base = jsclass;
this._super = jsclass.prototype;
_p.className = _cn;
return _p; // 返回原型
} catch(ex) {
_throw(0x000E, "JSVM/_extends:" + _cn + " 继承类错误{" + ex.description + "}");
}
};

JSVM是直接给所有Function对象增加一个共用的方法_extends来实现继承,所有的类都可以通过 类名._extends("js.lang.Object")的形式来把某个类继承下来。
在继承函数里不仅使用原型继承,还同时做了许多工作,给做继承操作的子类增加了_base(父类对象)、_super(基类对象)、className(本类的类名),如果出现了任何错误还进行相关的例外处理。


待续。。。
  回复  更多评论
  
只有注册用户登录后才能发表评论。

<2024年4月>
31123456
78910111213
14151617181920
21222324252627
2829301234
567891011

常用链接

留言簿(15)

随笔分类(1)

随笔档案(90)

文章分类(727)

文章档案(712)

相册

收藏夹

http://blog.csdn.net/prodigynonsense

友情链接

最新随笔

搜索

  •  

积分与排名

  • 积分 - 457968
  • 排名 - 6

最新随笔

最新评论

阅读排行榜

评论排行榜