Save Yourself Some Typing with ‘With’

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.

You May Also Like

About the Author: Kas Thomas

Leave a Reply