你了解“意外处理”这个概念吗?意外处理是高级编程语言(比如 C++、 Java 以及 VBScript)的一个重要功能,它负责控制当应用程序发生错误时,应该执行什么操作。独立的 JavaScript 并不支持意外处理,这导致一些编程人员更喜欢使用 VBScript 而不是 JavaScript。
但是自从出现了IE5,JavaScript 的这方面功能得以补充且增强。通过向语法中增加意外处理功能,Microsoft 将 JavaScript 提升到 C++ 和 Java 的语言标准。我们有理由相信:一些 VBScript 编程人员将很快转移到 JavaScript 的阵营中。
详细的内容是:
如何编写一个 JavaScript 导致意外的意外处理代码
如何编写一个用户导致意外的意外处理代码
如何在应用程序中编写用户导致意外的意外处理代码
JavaScript 、C++ 和 Java 的意外处理区别
JavaScript 和 VBScript 的意外处理区别
如何避开 JavaScript 和 VBScript 之间的矛盾
在许多编程语言中,当试图执行一个非法指令时,代码都会导致一个运行时间意外。我们可能经历的一个最微不足道的非法操作就是“除以0”错误。JavaScript 不会执行这一操作,它将给结果变量赋值为无穷大。其它的非法操作对于其它编程语言也很普遍,包括 JavaScript。访问一个空指针或一个空对象也是一个例子,访问一个超出数组合法范围的数组元素是另一个例子。
对于开发人员而言,通常是争取避免发生系统错误的。系统信息通常很含糊,一般的用户看不懂。事实上,系统错误将严重影响一个程序员的声誉,因为它们是臭虫(Bug)的清楚明白的证据。我们希望在代码中查找这些错误,在它们攻击操作系统之前发现它们以避免产生错误信息。在代码中查找意外会造成代码臃肿。当语言不支持意外处理时,将正常的代码流和错误查找混合在一起在所难免。以下的代码将查找两个错误 - 访问一个空对象和超出范围的数组元素: var newObject = createObject(dataPiece1, dataPiece2, dataPiece3);
if (newObject == null) {
// do something here when the object was not created
...
}
else {
// getElementIndex return an index or -1 for an error
var index = newObject.getElementIndex();
if (index == -1) {
// handle the error case
...
}
else {
{
// finally, you can do something with index
...
}
在 JavaScript 中支持意外处理的关键所在(在其它语言中也是一样,本文后面将做解释)是 try...catch 语句。 Try 这个关键字引导着一块可能会引起意外的正常处理代码,Catch 关键字引导着一块意外处理代码。一旦 try 这个区中引发了一个意外,程序流程的控制就切换到跟随它的第一个 catch 区域。但是一旦由 JavaScript 引发(这正是程序员所反对的),就没有机制能够断定错误的类型。以下是用来模拟上面流程的 try...catch 代码区: try {
var newObject = createObject(dataPiece1, dataPiece2, dataPiece3);
var index = newObject.getElementIndex();
// do something with index
}
catch {
alert("System Error; Call your sw vendor");
}
下面是另一个例子,可以在你的计算机上实际运行: < HTML>
< HEAD>
< TITLE> example 1 < /TITLE>
< /HEAD>
< BODY>
< IMG SRC="doc.gif">
< SCRIPT LANGUAGE="JavaScript1.2">
< !--
try {
for (image=0; image < document.images.length + 2; image++) {
alert(document.images[image].border);
}
}
catch (kuku) {
alert("We have experienced a JavaScript error");
}
// -->
< /SCRIPT>
< /BODY>
< /HTML>
这个例子中执行的意外是访问数组边界外的一个数组元素。在页面(doc.gif) 上,图形数是length (1),所以使用的最大指针是length+1 (2)。请注意传递给 catch (捕捉)区的变量kuku。JavaScript允许传递任何变量类型,在以后的页面中将做解释。虽然我们并不使用这个变量,但你必须要指定一个。
处理程序员导致的意外
并不是所有的意外都由 JavaScript 导致,还有一些是由程序员有意制造的。事实上,好的软件工程师把包含大量意外代码的使用作为简化代码的一种方式。
其中的原因在于:这样处理后,正常处理流就同查错部分分开了,从而使代码可读性更强,更易于维护和修改。每当要处理一个特定错误时,就可以制造一个意外。你不必使用 if...else 就能在正常流和意外处理流之间切换。一旦一个意外已经发生,流控制就切换到下一个 catch(捕捉)区,在那里进行判断和错误处理。由于程序员完全明白哪个意外是他自己制造的,因此他可以设计这样一个机制,很容易地计算出哪个是意外并向用户提供有用的信息。
下面这个简单的例子说明了你可以使用的意外处理机制: < HTML>
< HEAD>
< TITLE> example 1 < /TITLE>
< /HEAD>
< BODY>
< SCRIPT LANGUAGE="JavaScript1.2">
< !--
function createException(msgNum, msgText) {
this.messageNumber = msgNum;
this.messageText = msgText;
}
function triggerException() {
exceptionObj = new createException(1, "error blablabla");
throw exceptionObj;
}
function raiseException() {
try {
triggerException();
}
catch (exceptionObj) {
if (exceptionObj instanceof createException) {
alert("Call the programmer ASAP")
}
else {
throw exceptionObj;
}
}
}
raiseException();
// -->
< /SCRIPT>
< /BODY>
< /HTML>
主程序只有一行脚本:raiseException()。正如函数名所表示的,这个程序的唯一目的就是要生成一个意外。raiseException() 函数的主体部分包含 try...catch 语句: function raiseException() {
try {
triggerException();
}
catch (exceptionObj) {
if (exceptionObj instanceof createException) {
alert("Call the programmer ASAP")
}
else {
throw exceptionObj;
}
}
}
try 代码区创建一个意外对象并触发它: function triggerException() {
exceptionObj = new createException(1, "error blablabla");
throwexceptionObj;
}
意外对象
意外对象是一个规则的 JavaScript 对象。这时我们将对象命名为 exceptionObj,把它命名为 kuku 也可以。这个意外对象的目的是把数据从 try 代码区运送到 catch 代码区,以将其破译并向用户报告错误的细节。无论你希望意外对象包含多少属性和方法都可以。在我们的例子中定义了两个属性:错误的信息数和其文本信息。对象创建函数是规则的JavaScript: function createException(msgNum, msgText) {
this.messageNumber = msgNum;
this.messageText = msgText;
}
catch(捕捉)代码区只有一个参数,可以是任何数据类型。
传递到 catch 代码区的对象与错误数和错误信息相通讯。
注意首先要进行对象类型确认:
if (exceptionObj instanceof createException)
为了区别 JavaScript 和程序员查出的错误,这种确认是必要的。
正如它的名字所说明的,catch 代码区捕捉相关的 try 代码区出来的所有意外。
为了让用户获得正确的错误信息,
我们需要确定意外是由 JavaScript 导致的(例如访问超出范围的数组元素)还是由程序员导致的。
最好的方法是证实意外对象是否与我们创建它们所用的对象属于同一类型。
如果类型相匹配,我们就可以肯定意外是要我们去处理的。在这里的例子中,仅向用户显示了一个信息:
if (exceptionObj instanceof createException) {
alert("Call the programmer ASAP")
}
如果类型不匹配,我们就继续向上制造意外。
意外处理是嵌套的。
你可以将一个 try...catch 语句放在另一个 try 代码区里,
这样每当从第一个 catch 代码区制造一个意外时,它就被其母 catch 代码区所捕捉。
在我们的例子中,由于没有更高层次的 try...catch 语句,因此意外就由操作系统所处理。
操作系统负责处理那些以前没有被更低层次的 catch 代码区处理过的意外。
下面是我们例子中的意外抛出:
else {
throw exceptionObj;
}
下面我们来增强一点脚本功能。在上面的脚本中,我们指定了信息数以及创建意外对象时的描述:
exceptionObj = new createException(1, "error blablabla");
但是这时 triggerException() 和 raiseException() 函数只适用于一种意外类型。
为了让它们更通用一些,我们将错误数和文本参数增加到上面的两个函数中:
< HTML>
< HEAD>
< TITLE> example 1 < /TITLE>
< /HEAD>
< BODY>
< SCRIPT LANGUAGE="JavaScript1.2">
< !--
function createException(msgNum, msgText) {
this.messageNumber = msgNum;
this.messageText = msgText;
}
function triggerException(messageNum, messageTxt) {
exceptionObj = new createException(messageNum, messageTxt);
throw exceptionObj;
}
function raiseException(num, message) {
try {
triggerException(num, message);
}
catch (exceptionObj) {
if (exceptionObj instanceof createException) {
alert("Call the programmer ASAP")
}
else {
throw exceptionObj;
}
}
}
raiseException(1, "Doc JavaScript Column 36");
// -->
< /SCRIPT>
< /BODY>
< /HTML>
程序员制造的意外:一个真实的例子(一)
现在,我们使用前面页面中的意外处理函数来处理一个更加实际的例子。假设你的一个页面中有一个
< IMG> 标记,程序接收一些属性名并提取出相应的图形对象属性值。意外有可能是由访问不正确的属性和范围以外的数组元素引起的。下面是完整的代码: < HTML>
< HEAD>
< TITLE> example 1 < /TITLE>
< /HEAD>
< BODY>
< IMG SRC="doc.gif">
< SCRIPT LANGUAGE="JavaScript1.2">
< !--
function createException(msgNum, msgText) {
this.messageNumber = msgNum;
this.messageText = msgText;
}
function triggerException(messageNum, messageTxt) {
exceptionObj = new createException(messageNum, messageTxt);
throw exceptionObj;
}
function raiseException(num, message) {
try {
triggerException(num, message);
}
catch (exceptionObj) {
if (exceptionObj instanceof createException) {
alert("Call the programmer ASAP")
}
else {
throw exceptionObj;
}
}
}
fieldNames = new Array("border", "complete", "height", "hspace",
"lowsrc", "name", "src", "vspace", "width");
try {
for (image=0; image < document.images.length + 1; image++) {
for (i=0; i < fieldNames.length; i++) {
if (document.images[image] == null)
triggerException( 98, "illegal access to images array");
if (eval("document.images[image]." + fieldNames[i]) != null)
alert(fieldNames[i] + " is " + eval("document.images[image]." + fieldNames[i]))
else triggerException(i, "field name " + fieldNames[i] + " is not legal");
}
}
}
catch (exceptionObj) {
if (exceptionObj instanceof createException) {
if (exceptionObj.messageNumber > 0 && exceptionObj.messageNumber < 9)
alert("Problem in property: " + exceptionObj.messageText)
else if (exceptionObj.messageNumber < = 98
&& exceptionObj.messageNumber >= 97)
alert("Problem in images array: " + exceptionObj.messageText)
else raiseException(99, "Catch All");;
}
}
//raiseException(1, "Doc JavaScript Column 36");
// -->
< /SCRIPT>
< /BODY>
< /HTML>
程序员制造的意外:一个真实的例子(二)
首先我们定义数组 fieldNames。Try 代码区包括两个嵌套循环,外面一个在页面的图形之间循环:
for (image=0; image < document.images.length + 1; image++) {
里面的循环在 fieldNames 元素之间循环:
for (i=0; i < fieldNames.length; i++) {
我们在循环内部检查2个意外。第一检验是否有访问范围外数组元素的尝试。当有一个无效尝试时,我们起动一个意外:
if (document.images[image] == null)
triggerException( 98, "illegal access to images array");
第二个检验打印相关对象属性的值或当域名与任何对象的域名都不匹配时,起动一个意外:
if (eval("document.images[image]." + fieldNames[i]) != null)
alert(fieldNames[i] + " is " + eval("document.images[image]." + fieldNames[i]))
else triggerException(i, "field name " + fieldNames[i] + " is not legal");
catch(捕捉)代码区对意外对象进行解密。首先我们检验对象是否是正确的类型:
if (exceptionObj instanceof createException) {
前面已经解释过,这种检验对于区分 JavaScript 错误和用户错误是至关重要的。
一旦我们知道它是一个用户导致的意外后,我们就检验错误数是否在1―9之间,并打印一个适当的信息:
if (exceptionObj.messageNumber > 0 && exceptionObj.messageNumber < 9)
alert("Problem in property: " + exceptionObj.messageText)
我们检验错误数是否是97或98,并打印一个与意外相关的信息:
else if (exceptionObj.messageNumber < = 98
&& exceptionObj.messageNumber >= 97)
alert("Problem in images array: " + exceptionObj.messageText)
如果意外对象不与任何错误类型相匹配,我们就触发一个用操作系统处理的普通意外:
else raiseException(99, "Catch All")
一定要记住,如果你不处理这个意外,别人就会处理。操作系统会处理所有没人理会的意外。
与 Java、 C++ 之比较
请不要吃惊,你会发现:Java 和 C++ 的意外处理语法与 JavaScript 的非常相似。
同 JavaScript 中一样,它们也是以 try..catch 语句为中心的。主要的区别在于 catch 的变量。
就象前面页面中所解释的一样,在 JavaScript 中这个变量可以是任何数据类型,
可以是一个对象、一个字符串、一个整数或任何其它被支持的类型。
实际上,JavaScript 不支持类型声明,所以就无法声明 catch 的变量类型。
在 Java 中,catch 的变量是 Java 类的一个例示,将标准 Java 扩展成可导致的类。
Catch 语句应该是这样的:
catch (objectType exception)
其中意外是一个 objectType 类型的对象。
C++ 就不是那么限制了,Catch 的变量可以是标准 C++ 意外类的一个例示,或者是任何 C++ 类型:
catch (char *message)
由于在 JavaScript 中没有类型声明,在每个 try 代码区它只支持一个 catch 代码区。
在前面页面中已经显示了,当我们的意外处理要依赖于变量类型时,
我们就使用 instanceOf 函数来检查它是否是正确的类型。
在 C++ 和 Java 中,对每一个 try 代码区你可以使用多个 catch 代码区。
每个 catch 代码区负责处理适当类型的意外。假如你想处理 char 类型和 myException 类型的意外:
try {
// following function can throw exceptions of type char or myException
doSomeThingUseful();
}
catch (char *exceptionString) {
// handle the case that exception is a string
}
catch (myException exceptionObject)
// handle the case that exception is an instance of type myException
}
在 JavaScript 中,你要用这些代码行:
try {
// following function can throw exceptions of type char or myException
doSomeThingUseful();
}
catch (exception) {
// verify exception is of myExceptionObject type
if (exception instanceOf myExceptionObject) {
if (exception.kind == "string") {
// handle the sting exception
}
else if (exception.kind == "myException") {
// handle the object exception
}
}
另一个区别是 Java 和 C++ 支持意外 catch-all(全捕捉),也就是在一个 catch 代码区捕捉全部的意外。在 Java 中的语法是:
catch (Throwable t) {
// Handle all cases
}
其中 Throwable 是一个事先定义的 Java 类。在 C++ 中相应的 catch-all 语法是:
catch ( ... ) {
// Handle all cases
}
正如上面所说过的,由于 JavaScript 只支持一个 catch 代码区,根据定义,
所有的意外都是由同一个 catch 代码区捕捉的。你应该使用:
catch ( exception ) {
// Handle all exceptions
}
同样的区别在 C++、 Java 和 JavaScript 的导致语句中也存在。在三种语言中语法是一样的:
throw exceptionObject;
由于 Java 只能捕捉 Throwable 类型的意外, exceptionObject 必须是这种类型。
C++ 和 JavaScript 可以抛出任何数据类型,包括字符串、整数、对象等。
C++ 和 Java 在意外向上繁殖的方式上与 JavaScript 也不同。
如果一个意外不在 C++ 或 Java 中处理,那么在调用方案中,它就自动被提升到一个更高层次,如调用函数、主程序或浏览器。
在 JavaScript 中,如果你希望一个更高层次处理这个意外的话,就不得不自己来提升这个意外。
前面页中已经显示过,你应该使用这个简单的语法:
throw exceptionObj;
最后一个区别是 C++ 和 Java 不支持 try..catch 代码区的嵌套。
而在 JavaScript 中,你可以将一个 try...catch 语句装在另一个语句的 try 或 catch 区内。
与 VBScript 相比较
VBScript 对意外处理的支持程度要低于 JavaScript 的新意外处理方式。
如果到现在还因为喜欢其错误处理而坚持使用VBScript 的话,你就没有充分的理由了,因为 JavaScript 现在可以处理意外了。
VBScript 意外处理的原理很简单,调用函数然后检验是否有错误。
JavaScript 是用 catch 语句来捕捉意外的,而 VBScript 是用 On Error 语句来捕捉的。
省略掉 On Error 语句会将所有意外传播到浏览器,浏览器随之向用户显示一个错误对话框。
事实上, JavaScript 也有这种行为。
如果不使用 try...catch 语句,所有意外都会由浏览器来处理并向用户显示一个错误信息。
前面已经解释过,用户并不十分清楚他该向哪里去,因此必须要赶在浏览器之前对意外进行处理。
如果考虑为用户处理意外,这样的 Web 站点就属于等级较高的。
On Error 语句很直观: On Error Resume Next
这个语句只是打开了错误处理。如果接下来的行中的任何位置出现了意外,
运行就从紧接着出错行的那行程序继续执行,出错行指的是你调用了引起意外的函数的行。
在 JavaScript 中与此相当的是 try...catch 语句: try {
// Exceptions in this block will be handled by the following catch block.
// Any exception will quit this block and switch control to the catch block
}
catch (exceptionObj) {
// Handle exception from the try block above
// Code in this block will be executed before the line immediately
// following the culprit line that caused the exception
}
在 VBScript 和 JavaScript 中捕捉意外的主要区别是:一旦你打开了错误处理,在 VBScript 中就不能将其关闭。
在VBScript中,错误处理只能在函数结束时才能关闭。
而在 JavaScript 中,你可以将你想要打开错误处理的那个语句区包含在 try 代码区内。
简单地说,在VBScript 和 JavaScript 中,你都可以控制一个区的开始。
而只有在 JavaScript 中你才能控制一个区的结束。
在 VBScript 中,区的结束就是函数的结束。
在 VBScript 中处理意外比捕捉它们要复杂得多。在每个可疑语句之后,你都要明确地检查错误: ` turn on error handling
On Error Resume Next
` clear the Err object
Err.Clear
` The following statement or procedure call might cause an exception
Kuku()
` Check if an exception was raised
If Err.Number > 0 then
` handle the exception here
End If
你已经注意到了 Err 对象。 VBScript 中的意外处理就是以它为基础的。
它有6个属性是由最近的意外设置的:
Number(数量)、 Description(描述)、 Source(来源)、
HelpFile(帮助文件)、 HelpContext(帮助上下文)、 LastDLLError(上一个DLL错误)。
JavaScript 的 Error 对象共享这些属性中的两个: