In JavaScript1.4, new methods for error-handling are introduced (throw
, catch
).
JavaScript1.5 introduces a new object constructor, the Error()
constructor,
which we theoretically can use to define our own error codes. But neither of these are
currently available to Internet Explorer users... and even diehard Netscape fans (like
yours truly) find no simple exit to a function. Let's review our ineq
function again:
function ineq(arg1,sign,arg2) { var temp = 0 var temp3 = 0 if (arguments.length%2==1) { var temp2 = false } else { var temp2 = true } var ineqs = new Array() for (temp = 0; (temp2==true)&&(temp < arguments.length); temp++) { if (temp%2 == 0) { ineqs[temp] = arguments[temp] if (ineqs[temp][0]=="NaN") { temp2 = false } } else { ineqs[temp] = arguments[temp] temp2 = false for (temp3 = 0; temp3 < INEQOP.length; temp3++) { if (INEQOP[temp3]==ineqs[temp]) { temp2 = true } } } } for (temp = 1; (temp < ineqs.length)&&(temp2 == true); temp+=2) { temp2 = false if ((ineqs[temp]==LT)&&(ineqs[temp-1].isLesser(ineqs[temp+1]))) { temp2 = true } if ((ineqs[temp]==EQ)&&(ineqs[temp-1].isEqual(ineqs[temp+1]))) { temp2 = true } if ((ineqs[temp]==GT)&&(ineqs[temp-1].isGreater(ineqs[temp+1]))) { temp2 = true } if ((ineqs[temp]==LTE)&&(!ineqs[temp-1].isGreater(ineqs[temp+1]))) { temp2 = true } if ((ineqs[temp]==IEQ)&&(!ineqs[temp-1].isEqual(ineqs[temp+1]))) { temp2 = true } if ((ineqs[temp]==GTE)&&(!ineqs[temp-1].isLesser(ineqs[temp+1]))) { temp2 = true } } return temp2 }
Ideally, once temp2
becomes false outside a for-loop, there should be no
need to execute any other statements. Still, it does evaluate each follow-up for-loop's
initial line once. This is not entirely efficient. Imagine what happens when we render the
flag false early in a much larger validation. It still goes through a lot of statements.
The break statement can help within a loop to abort the loop. The radioval()
function from the previous lesson can be altered here:
function radioval(obj) { var flag = -1 for (var k = 0; k < obj.length; k++) { if (obj[k].checked) { flag = k break; } } return flag }
In the spirit of flags, I could just as easily have used:
function radioval(obj) { var flag = -1 for (var k = 0; (k < obj.length)&&(flag==-1); k++) { if (obj[k].checked) { flag = k } } return flag }
But there is a more advanced way to exit a function in a controlled manner, and even to
provide a central area for reporting errors to the user. Labels can be used to designate a
loop, and a break statement can be used to exit just after the loop. Imagine this scenario
for the radioval()
function:
function radioval(obj) { radioval_func: for (var radioval_loop = 1; radioval_loop < 2; radioval_loop++) { var flag = -1 for (var k = 0; k < obj.length; k++) { if (obj[k].checked) { flag = k break radioval_func; } } } return flag }
At first, you might wonder about the new outer loop -- after all, it will only run
once. But the break
statement breaks all loops out to the radioval_loop
loop. Nothing between the break
statement and the rest of the outer loop will
execute. So this "useless" loop allows us to control the flow of the function
from inside the inner loops.
Try this demo:
Of course, breaking the outer loop doesn't help us much in the radioval()
function. But in the larger, and more complex ineq()
function...
function ineq(arg1,sign,arg2) { ineq_func: for (var ineq_loop = 1; ineq_loop < 2; ineq_loop++) { var temp = 0 var temp3 = 0 if (arguments.length%2==1) { var temp2 = false break ineq_func; } else { var temp2 = true } var ineqs = new Array() for (temp = 0; (temp2==true)&&(temp < arguments.length); temp++) { if (temp%2 == 0) { ineqs[temp] = arguments[temp] if (ineqs[temp][0]=="NaN") { temp2 = false break ineq_func; } } else { ineqs[temp] = arguments[temp] temp2 = false for (temp3 = 0; temp3 < INEQOP.length; temp3++) { if (INEQOP[temp3]==ineqs[temp]) { temp2 = true } } } } if (temp2 == false) { break ineq_func; } for (temp = 1; (temp < ineqs.length)&&(temp2 == true); temp+=2) { temp2 = false if ((ineqs[temp]==LT)&&(ineqs[temp-1].isLesser(ineqs[temp+1]))) { temp2 = true } if ((ineqs[temp]==EQ)&&(ineqs[temp-1].isEqual(ineqs[temp+1]))) { temp2 = true } if ((ineqs[temp]==GT)&&(ineqs[temp-1].isGreater(ineqs[temp+1]))) { temp2 = true } if ((ineqs[temp]==LTE)&&(!ineqs[temp-1].isGreater(ineqs[temp+1]))) { temp2 = true } if ((ineqs[temp]==IEQ)&&(!ineqs[temp-1].isEqual(ineqs[temp+1]))) { temp2 = true } if ((ineqs[temp]==GTE)&&(!ineqs[temp-1].isLesser(ineqs[temp+1]))) { temp2 = true } } } return temp2 }
I've added three break ineq_func;
statements. In the design of this
particular function, designed to verify compound inequalities (a < x < b <...)
these breakpoints do not really save much time. But these breakpoints indicate invalid
arguments, whereas the final loop performs the actual inequality test. Wouldn't it be nice
to include some sort of error message for an invalid function call?
function ineq(arg1,sign,arg2) { var ineq_err = 0 ineq_func: for (var ineq_loop = 1; ineq_loop < 2; ineq_loop++) { var temp = 0 var temp3 = 0 if (arguments.length%2==1) { var temp2 = false ineq_err = 1 break ineq_func; } else { var temp2 = true } var ineqs = new Array() for (temp = 0; (temp2==true)&&(temp < arguments.length); temp++) { if (temp%2 == 0) { ineqs[temp] = arguments[temp] if (ineqs[temp][0]=="NaN") { temp2 = false ineq_err = 2 break ineq_func; } } else { ineqs[temp] = arguments[temp] temp2 = false for (temp3 = 0; temp3 < INEQOP.length; temp3++) { if (INEQOP[temp3]==ineqs[temp]) { temp2 = true } } } } if (temp2 == false) { ineq_err = 3 break ineq_func; } for (temp = 1; (temp < ineqs.length)&&(temp2 == true); temp+=2) { temp2 = false if ((ineqs[temp]==LT)&&(ineqs[temp-1].isLesser(ineqs[temp+1]))) { temp2 = true } if ((ineqs[temp]==EQ)&&(ineqs[temp-1].isEqual(ineqs[temp+1]))) { temp2 = true } if ((ineqs[temp]==GT)&&(ineqs[temp-1].isGreater(ineqs[temp+1]))) { temp2 = true } if ((ineqs[temp]==LTE)&&(!ineqs[temp-1].isGreater(ineqs[temp+1]))) { temp2 = true } if ((ineqs[temp]==IEQ)&&(!ineqs[temp-1].isEqual(ineqs[temp+1]))) { temp2 = true } if ((ineqs[temp]==GTE)&&(!ineqs[temp-1].isLesser(ineqs[temp+1]))) { temp2 = true } } } switch (ineq_err) { case 0 : break; case 1 : var errmsg = "Incorrect formatting in ineq: " errmsg+="\nRequires odd number of arguments.\na < x < b" alert(errmsg) break; case 2 : var errmsg = "Incorrect formatting in ineq: " errmsg+="\nNumber.NaN cannot be compared in inequality." alert(errmsg) break; case 3 : var errmsg = "Incorrect formatting in ineq: " errmsg+="\nRequires valid inequality code" errmsg+="\nLT <, EQ ==, GT >, LTE <=, IEQ !=, GTE >=" alert(errmsg) break; default : alert("Unscheduled error in ineq.") break; } return temp2 }
In case you're wondering, the ineq_err
variable is another flag. This one
is used to detect errors the developer sets in place. The switch
statement,
covered in another tutorial at
JavaScript Kit, enables us to then report errors in a controlled manner at the end of
the function. By using the break
statements earlier, we ensure it goes
directly from the error to the error message, and then to the end of the function.
The system above has the following key features:
funcname_loop
loop runs only once, and is labeled as funcname_func
.
break funcname_func;
statements are always preceded by a line
setting the error code variables appropriately (such as funcname_err
).
In conclusion, I wish I had discovered this sooner. Another function of mine, used for division of numbers with lots of digits, gets stuck in an endless loop if a particular condition occurs. It's extremely deep within the function -- trapped within about three or four for-loops. I've worked very hard to prevent that condition from happening, which is something internal to the function (a bug). Because of the validation requirements already in place, it cannot occur from a user or developer feeding bad arguments into it. I'll be revising that particular function soon (along with several others) to take advantage of these cross-browser features Real Soon Now (meaning when I get around to it)