我承认,我是 JS 服务端技术的鼓吹者,我企图将 JS 语言的应用“从客户端包围服务端”,况且这不是新鲜事(SSJS),与我保持同一意见的人应该是有的,尽管对此不会有太大的热情,或者没有为此而“雄辩”的理由,但也藏匿不少的,——我想,通过本文的介绍,他们也会在默默地认同下文的说法。
ASP 是十多年前微软发布的技术——刻下还有多少 ASP+COM/Dll 的生产环境呢? 不知道了~。道不清是不是“无心插柳柳成荫”呢,我觉得能在 ASP 中找到 JS 的身影,对于我和我希望使用 JS 的态度而言,简直就真是“踏破铁鞋无觅处”了,呵呵,——虽然这里的“柳成荫”未必达到所形容的程度。Classic ASP (JScript) 既然是老技术,我想,根本没有“哗众取宠”的资本,那么即使鼓吹一下亦无伤大雅吧。
这是人家翻译老外的文章,也我博客当前为数不多转载的文章。在硬盘上呆许久了,想要清理一下旧档案的时候,发现该段文字还在,——我想,还是发布到这里吧,不至于彻底删掉。
2013-06-13 Edit:发现一国人撰写的 ASP 服务端框架,开源:https://code.google.com/p/js-asp/。
在 Active Sever Page 首次发布的时候,有两种内置的语言可以让你来编写 ASP 脚本或 HTAs:VBScript 和 JScript。 在 ASP 1.0 发布的时候,除了 VBScript 和 JScript 外并没有提供多少方法。 到了 ASP 2.0, 不过,随着 Windows 宿主主机(ASP Host)和 HTML 应用程序(HTAs)的升级,这种情况得以改变。你可以通过安装第三方的工具来实现,例如 Perl,但是操作系统本身并不提供内置的 脚本功能。
一 些开发者已经选择了其他语言(例如 PHP 或者 RoR),而且微软通过创建 ASP.Net 也开创了一个新的方向。这些都是不错的选择,但是它们都不是内置在 操作系统中,而且通常情况下,能够具有共同的最底层的管理平台是非常重要的(例如有的机器上你不能安装 .Net Framework)。尽管 .Net 很强大,也有很高的入门门槛(必须是Windows XP SP2 以后的版本,安装了 .NET 2.0)。相比之下,一个使用 VBScript 或 JScript 编写的 ASP 脚本无需任何条件就可以运行在 Windows 2000 及以后的版本上。两者有一个共同优势就是它们都内置在 Windows 的 asp.dll 内核中。
既然大多数 ASP 开发者将需要编写 ASP 页面脚本,那么我们就有必要来选择可以帮助你顺利完成工作的最佳语言:JScript。
理由一:JScript被更广泛的使用
根据我的经验,关于 JScript 是什么很多人比较模糊。简单的说来,JScript是微软对 ECMA-262 规范的实现,这个规范描述了一个被称作 ECMAScript 的脚本语言。JavaScript 和 JScript 是两个应用最广泛的 ECMAScript 规范的实现。除了不具有一些方便COM编程的属性扩展之外,JScript 现在与 JavaScript 差不多(JScript.NET 是另一种不同的语言,超出了本文的说明范围)。
作为免版税的技术,JavaScript/JScript 语言被广泛应用在不同平台的不同应用程序中,最重要的应用是在浏览器中。其他厂商也已经把它们作为自动化语言(例如 Adobe CreativeSuite/AIR)使用。换言之,JScript知识可以被利用在很多不同的地方。相比之下,VBScript 则是专属于微软的,只有在使用 VBScript 解释器的 Windows 平台上的微软应用程序才会用到。
理由二:JScript还在不断发展
正如上面我所提到的,JScript 是基于 ECMA-262 标准的。尽管当前版本的 JScript 等同于 ECMA-262 3.0,即将推出的 ECMA-262 3.1 将是向后兼容 3.0 的。如果 ECMA-262 3.1 被广泛的接受的话,微软也会为了维持浏览器的兼容性而将对 JScript 进行升级。
相比之下,微软对于 VBScript 则是处于“保持不变”的状态。这意味着微软将只是修补一些在 VBScript 解释器中的大错误和安全漏洞;它不会去对其增加一些新的功能或对语言进行扩展。既然 VBScript 处于维持阶段,学习一个更有未来的语言对开发者来说更有意义。
理由三:JScript更易学
VBScript 被广泛应用的一个是人们认为 VBScript 更易掌握。因为种种原因,对比之下人们认为 JScript 要难学一些。这可能是因为 JScript 的 C 编程风格与 VBScript 有些不一样。但是,实际上你一旦入了门,JScript 也是很容易掌握的。
许多基本的概念是相同的(例如声明、变量和函数等);它们只是看上有些不同而已。一旦你了解了这个语言的基础构成,使用 JScript 来代替 VBScript 来进行脚本编程是一件很简单的事情。 和大多数 Windows 脚本编程作者一样,我开始使用的时候 VBScript,但是我发现,虽然某些编程概念有所不同,JScript 和 VBScript 一样容易学习。
理由四:JScript 是面向对象的
JScript 是作为一个面向对象语言而建立起来的。内置的数据库类型(诸如字符串和数字)本身都是具有方法的对象。例如,数字类型有一个 toString() 方法,来将数字作为一个文本返回,而文本类型则有 toUpperCase 和 toLowerCase 方法来以大写或小写方式返回文本。使用它可以很容易以最少编码方式来创建你自己的对象,看一下下面的例子:
var options = { path: "", recurse: false };
这简单的一行代码就创建了一个没有名称的对象,它包含两个属性,并对属性进行了初始化。
在 VBScript 中你也可以创建一个用户定义的对象,但是它要复杂的多。这个语言要求你必须创建一个类,并初始化这个类,如下面代码所示:
Class OptionsClass Public Path Public Recurse Private Sub Class_Initialize() Path = ""Recurse = False End Sub End Class Dim Options Set Options = New OptionsClass ' Creates an instance of the class
JScript 的面向对象特点使这变成一个简单的任务;VBScript 则要求编写更多的代码来完成同样的事情。
JScript 还可以让你创建一个被命名的对象(这在 VBScript 中强制要求的);如果你需要多个这种对象的话,你可以简单的定义一个构造函数来定义这个对象。例如:
function optionsObject(path, recurse) { this.path = path; this.recurse = recurse; } var options = new optionsObject("c://winnt", true); // 创建一个实例对象
JScript 使用基于原型来进行继承,而不是基于类。使用一个对象的 prototype 属性,你可以扩展一个对象的行为,增加一个方法到它的 prototype 属性中。例如:
Array.prototype.exists = function(item) { for (var n = 0; n < this.length; n++) if (item == this[n]) return true; return false; }
以上代码增加了 exists 方法到这个 Array 对象中。下面是一个你如何使用这个新方法的示例:
var myArray = ["a", "b", "c", "d"]; // 定义一个包含四个元素的数组 var result = myArray.exists("a"); // 返回 true var result = myArray.exists("e"); // 返回 false
从这个例子我们可以看到,JScript 语言是多么灵活,无论你自己的或内置的对象,对它们扩展定制的方法是多么简单。
理由五:JScript 的正则表达式处理能力更强
正则表达式是 JScript 本身的一个功能;它们被内置在语言的语法中。而 VBScript 在版本5以前并不支持正则表达式,因此,它的语法相对来说非常麻烦和糟糕。举个例子来说,如果你想使用正则表达式来检查一个输入的文本是否是电子邮件格式。在 VBScript 中你不得不使用 New 这个关键字和 Set 声明来创建一个 RegExp 对象,然后配置它的属性来进行搜索。下面是一个例子:
Address = "foo@bar.com" Set RE = New RegExp With RE .Pattern = "/w+@/w+/./w+" .IgnoreCase = True End With Result = RE.Test(Address)
相同的 JScript 代码则显得更简洁。
var address = "foo@bar.com"; var RE = //w+@/w+/./w+/gi; var result = RE.test(address);
上面的第二行代码创建了一个 RegExp 对象。如果你在代码被执行之前知道模式的话,/表达式/语法是描述一个正则表达式模式的很方便的方法。作为一种选择,你可以使用 new 操作来创建一个RegExp 对象,如下面所示:
var address = "foo@bar.com"; var RE = new RegExp("//w+@//w+//.//w+", "i"); var result = RE.test(address);
如果你想在运行时描述一个正则表达式模式的话,这个语法是非常有用的。此外,JScript 的 RegExp 对象具有一个编译方法,当重复使用它们是可以提高性能,比如在循环中使用它的时候。
理由六:JScript 的数组要强大很多
数组一直是 VBScript 的心头之痛。它们非常不灵活,只有在这个数组在定义的时候没有指定一个固定的元素数量的时候,它才可以被重新指定大小(例如,如果你定义了一个具有五个元素的数组,那么你不能增加第六个元素到数组中)举个例子来说,看一下下面的 VBScript 代码:
Dim MyArray(4) ' 声明一个数组可以最多包含5个元素 MyArray(5) = "Test" ' 引发错误:这个数组只能容纳5个元素 elements ReDim MyArray(6) ' 同样引发错误:数组已被定义成固定大小
在 VBScript 中,你可以创建一个动态的可以修改大小的数组,但是这是一个两步过程;你不得不首先声明一个数组,然后你不得不对它重新指定大小,以来指定正确的元素数量:
Dim MyArray() ReDim MyArray(5) '重新定义数组大小 MyArray(5) = "Test" '给数组的第六个元素赋值
如果你想使用一个 VBScript 数组来容纳从一个循环中读出的变化数量的元素(例如一个从 FileSystemObject 的文件集合得到的 File 对象数组),你将需要定时的使用带有 Preserve 关键字的 ReDim 声明来重新定义这个数组的大小。(考虑到性能的原因,你可能不想在每一次循环完成一次后使用 ReDim 。)
在 JScript 中,数组是一个对象类型,本质上是动态的,并提供了很多方法来操作它们。举个例子来说,push 和 pop 方法用来追加元素到数组中和从数组中删除元素,在过程中自动调整数组的大小。JScript 数组还有一个 sort 方法,来支持用户定义的排序功能。对于返回一个 VBScript 风格数组的 ActiveX 对象(JScript 将这些称作 SafeArrays 或VBArrays)来说,这个数组对象有一个 toArray 方法来转换 SafeArray 数组为一个JScript数组。
事实上,微软似乎创建了脚本运行时的 Dictionary 对象来克服这个 VBScript 数组数据类型的限制。因为我一直在使用 JScript 编写代码,我发现了这一点,只有一种情况下我需 要Dictonary 对象:从一个 JScript 数组返回一个 SafeArray 对象(奇怪的是,这个功能并不是内置支持的)。如下例所示:
function toSafeArray(arrayObject) { var dict = new ActiveXObject("Scripting.Dictionary"); for (var n = 0; n < arrayObject.length; n++) dict.Add(arrayObject[n], ""); return dict.Keys(); // Keys返回一个SafeArray }
当使用 ActiveX 组件的时候,有时候需要 SafeArrays,我使用了一个与上面的类似的函数,来转换一个 JScript 数组为 SafeArray。
理由七:JScript日期处理功能可以避免本地时间问题
在VBScript中,一个日期的值总是“本地”的。根据时间的来源不同,当计算日期的时候,这可能导致协调世界时(UTC)和夏令时间的错误。让我们来看一个实际的例子。pwdLastSet活动目录(AD)用户属性被存储为自12:00am January 1, 1601, UTC以来的一个间隔数字。以下是一个如何转换pwdLastSet值到一个VBScript日期的示例代码。
Dim UserDN, ADsUser, LastSet, NanoSecs, VBTime UserDN = "CN=John Smith,OU=Sales,DC=wascorp,DC=net" Set ADsUser = GetObject("LDAP://" & UserDN) Set LastSet = ADsUser.pwdLast Set NanoSecs = (LastSet.HighPart * (2 ^ 32)) + LastSet.LowPart VBTime = CDate(#1/1/1601# + (NanoSecs / 600000000 / 1440)) ' Returns UTC time
这个脚本片段连接到活动目录中的John Smith的用户帐号对象,并返回帐号的pwdLastSet属性。(Set声明是需要的,因为pwdLastSet值是作为一个对象返回的。)接下来,代码通过读它的HighPat和LowPart属性,将pwdLastSet的值转换为一个单个32位值。(这个转换只是一个近似值,但是我们现在忽略这个微小的差别。)最后,代码增加这个天数到12:00am January 1, 1601,从而获得最终的结果。
这个结果得出来的值是在UTC中的,VBScript没有提供方法来转换这个日期为本地时间。我曾经看到一些示例代码通过从注册表中阅读本地时间偏移来调整到本地时间的日期。但是这是一个非常麻烦而且脆弱的解决方案,首先,它假定运行这个脚本的帐号具有权限可以阅读注册表的值,其次它假定注册表的值从来没有被改变,第三,它不能适应夏令时的调整,不知道返回的结果是不是在经过一个DST改变后的。举个例子来说,假若我们要计算将来一个用户的密码在将来要失效的日期和时间,当前的本地时间偏移可能会在将来因为经过了DST修改,从而变得不准确。
在JScript中,日期被Date对象来处理。JScript的Date对象可用来表示任意日期和时间,获取当前系统日期,以及计算日期差。它有几种预定义的属性和方法。Date对象存储一周的某一天;月、日、年;以及以小时、分钟、秒、毫秒计的时间。此信息建立在自从协调通用时间(UTC)1970年1月1日00:00:00.000以来的毫秒数的基础上。以下是在JScript中的等效代码:
var userDN, aDsUser, lastSet, nanoSecs, jsTime; userdn = "CN=John Smith,OU=Sales,DC=wascorp,DC=net"; aDsUser = GetObject("LDAP://" + userDN); lastSet = aDsUser.pwdLastSet;nanoSecs = ((lastSet.HighPart * (Math.pow(2, 32))) + lastSet.LowPart); // Natively stored as UTC jsTime = new Date(Date.UTC(1601, 0, 1, 0, 0, 0, nanoSecs / 10000));
这个代码与上面VBScript代码完成同样的事情,它拷贝pwdLastSet的值到一个32位数值中。但是,注意代码的最后一行使用了Date对象的 UTC方法来建造一个UTC日期。最漂亮的是,当你取回这个值的时候,JScript知道如何将它转换成本地时间。我使用我自己域上的一个帐号来完成了这个实验。VBScript返回的时间是16 May 2007 15:12,而JScript返回的时间是16 May 2007 09:12。注意,JScript的答案是正确的,本地时间是9:12而不是15:12,因为我的本地时区比UTC早6个小时。
理由八:JScript有更好的异常处理机制
VBScript的异常处理机制非常简陋,它由两个程序声明组成:On Error Resume Next禁用了默认的错误处理机制,而On Error GoTo 0则启用了默认的错误处理机制。如果你禁用了默认的错误处理句柄后,一旦出现错误,VBScript更新Err对象的属性。这意味着假若你有一系列的可能引发错误的声明,你将不得不在每一个声明后面检查Err对象,只有这个Err对象的Number属性是0的时候,才能继续下面的代码。如下面的代码所示:
On Error Resume Next Dim FSO, TS Set FSO = CreateObject("Scripting.FileSystemObject") Set TS = FSO.CreateTextFile("C:/Logfile.csv") If Err = 0 Then TS.WriteLine("LastName,FirstName,Email") If Err = 0 Then LogData End If TS.Close() End If
上面的代码试图创建一个文本文件;假若它成功的话,然后它试图写一行文本到文件中。如果WriteLine方法成功执行的话,上面的脚本将只执行LogData?函数,如果你需要检查多重错误的话,要想跟踪脚本的逻辑是非常困难的。相比之下,JScript使用基于块的异常处理机制,使你可以很简单的处理错误,而不用在每一个声明中检查错误。与上面的代码等效的JScript代码如下:
var FSO, TS; FSO = new ActiveXObject("Scripting.FileSystemObject"); try { TS = FSO.CreateTextFile("C:/Logfile.csv"); TS.WriteLine("LastName,FirstName,Email"); logData(); TS.Close(); } catch(err) { // Put error-handling code here }
这个try{...}块说明了可能会产生错误的代码。如果一个错误出现的话,脚本的执行则跳到catch块中。err变量将包含一个包含详细错误信息的对象。JScript还支持一个finally块,它一般位于try和catch块后面,它包含在try程序块(还有catch块,如果错误出现的话)后一定要执行的代码。举个例子说,它可以被脚本来释放某个资源,无论是否出现一个错误。
结论
JScript内置在操作系统中的最好的Windows活动语言。在本篇文章中,我列举出了八个理由来说明为什么它比VBScript要强大。我认为,ASP开发者应该好好学习这个强大的语言。
另:可参见这块的讨论:《JScript 是最适合Ext 的 Server Side 语言》