PDF In-Depth

Save Yourself Some Typing with 'With'

August 05, 2001

Advertisement
Advertisement
 

One trick I've learned (the hard way) for reducing the number of bugs in my JavaScript is to make use of any and all tools and techniques for cutting down on the amount of typing I have to do. The less typing I do, the fewer typos I make (and the fewer typo-related bugs I have to chase down). It's that simple.

I make liberal use of cut-and-paste editing, which helps a lot with sections of code in which variable names get repeated a lot. But lately, I've been making use of an even handier technique for reducing the amount of typing I do. My new secret weapon: JavaScript's "with" statement.

In JavaScript, you can specify an object reference in parentheses after the word "with"; then, for whatever scope you want (as defined by curly braces bracketing your code) the object reference in question will be used by default to resolve variable names. A quick example will show you what I mean. Suppose you have the following code:

if (global.items > global.maxCount)
   global.items = global.maxCount;
   
else if (global.items < global.minCount) 
   global.items = global.minCount;

That's an awful lot of "global this" and "global that." To cut down on typing, you could instead write:

with (global) { 

if (items > maxCount)
   items = maxCount;
   
else if (items < minCount) 
   items = minCount;

} 

Within the scope of the curly braces, the JavaScript interpreter will try to resolve unknown variable names (like maxCount) against the "global" object. Hence, the code shown here is 100% equivalent to the lengthier version shown further above. But it took 56 fewer keystrokes to type; and the end result is more readable (hence easier to debug and maintain). A true win-win situation.

Iterating Through All Fields

A common situation in JavaScript is having to iterate through all fields in a form. Normally, a lot of typing is called for. The "with" construct can make things more manageable. Suppose you'd like to inspect every field in a form, and if the field name contains "Amt" (in any combination of upper or lower case), you want to accumulate its value into "subtotal." You could do it this way:

var subtotal = 0.0;
var fld;

with (this) { // default object is 'this'

   for (var k = numFields; k; k--) 
   {  
     fld = getField(getNthFieldName(k-1));
     if (fld.name.match(/amt/i))
        subtotal += fld.value;
   }
}

Let's look at it line by line. First we declare some scratch variables, then we immediately use with (this). The for loop is set up to loop through all fields in the document. We rely on a couple of C-programming idioms to streamline things: We declare the index variable 'k' in the for statement itself (perfectly legal in JavaScript); and we decrement from high index limit to low, breaking when 'k' reaches zero. The condition-check statement is simply 'k', by itself, meaning "evaluate 'k' and if it is true..." which is to say, non-zero, "continue."

Normally, I consider it poor practice to nest function calls, particularly when the function names get long and hairy, but in the case of the first inner-loop statement, I decided to make an exception, since the overall effect (in terms of readability) of nesting getNthFieldName() inside getField() doesn't strike me as bad at all. Of course, without the "with" statement, you'd be nesting this.getNthFieldName() inside this.getField(), which would be virtually unreadable.

The if statement takes the name of the field object and applies the String method, match(), to it to see if the substring "amt" occurs anywhere in the name of the field. The /amt/i construction is a regular expression meaning "match the exact letter combination that's shown here between slashes, but in case-insensitive manner." (The 'i' means case-insensitive.) If a match occurs, the field's value is accumulated into subtotal.

For Advanced JavaScripters

If you're an advanced JavaScript user, you may have spotted an even easier way to "clean up" the code example shown above. Technically speaking, the "with (this)" code is not necessary at all. That's because 'this' is the default object context in all situations (by definition). Methods that are parented off the Doc object will be defaulted to a 'this' reference automatically, if you should happen to leave it off. For example, the following two lines of code are 100% equivalent:

this.getField('a').value = 60;
getField('a').value = 60; // equivalent 

In the second case, the JavaScript runtime interpreter, not having been given the parent object reference for 'getField', will try 'this' as a default object scope (which will work, of course). As a stylistic matter, some would say it's poor form to omit the 'this' reference from a Doc object method. But all the top-level functions in your documents are parented off of the Doc object, and yet you never call a custom-written function using 'this' on the front of it; so I consider the matter moot. If you want to omit 'this' from your getField() calls, you can certainly do so. The only time you really need to specify the Doc object reference explicitly is when the field, property, or method you're interested in happens to be in another document (instead of in 'this'). But that will be rare.

Math Tip

The 'with' trick is especially handy for improving the readability of functions that make extensive use of Math methods. (JavaScript has a built-in Math class that has built-in constants and methods that can come in quite handy.) For example, consider the following:

// hard to read
myVariable = Math.abs(myVariable);
var root = Math.sqrt(myVariable); 
var y_position = Math.sin( degrees * Math.PI/180 );

Now consider putting Math in a "with" statement:

with (Math) {

myVariable = abs(myVariable);
var root = sqrt(myVariable); 
var y_position = sin( degrees * PI/180 );

}

This is a much cleaner way to go, wouldn't you say?

Limitations

The 'with' trick works only for objects whose properties (fields) and/or methods are already defined. That is, you cannot define new properties or attach new methods to an object inside a 'with' statement. Also, you can't use square brackets to refer to object properties inside a 'with' block:

var oscars = new Object();

oscars['Best Actor'] = "Kevin Spacey"; // legal

with (oscars) {

['Best Actor'] = "Tom Cruise"; // error!

} 

This minor limitation aside, I hope you'll agree with me that the 'with' statement is a useful construct for cutting down on keystrokes (and typos) while also making code more readable. As with any coding idiom, though, it's possible to overuse it, so save the 'with' statement for those occasions when it's most needed.

PDF In-Depth Free Product Trials Ubiquitous PDF

Debenu Quick PDF Library

Get products to market faster with this amazing PDF developer SDK. Over 900 functions and an equally...

Download free demo

Back to the past, 15 years ago! Open Publish 2002

Looking back to 2002, it's amazing how much of the prediction became a reality. Take a read and see what you think!

September 14, 2017
Platinum Sponsor





Search Planet PDF
more searching options...
Planet PDF Newsletter
Most Popular Articles
Featured Product

Debenu PDF Aerialist

The ultimate plug-in for Adobe Acrobat. Advanced splitting, merging, stamping, bookmarking, and link control. Take Acrobat to the next level.

Features

Adding a PDF Stamp Comment

OK, so you want to stamp your document. Maybe you need to give reviewers some advice about the document's status or sensitivity. This tip from author Ted Padova demonstrates how to add stamps with the Stamp Tool along with related comments.