1. Keywords

If you have browsed much Conkeror code then you have no doubt seen keyword arguments in action. They're used in many places in the Conkeror source, but they aren't a standard part of JavaScript — they're a language extension provided by Conkeror's keywords.js.

Keywords are a special kind of variable, distinguished by the fact that they start with a dollar sign ($). They are used to pass named arguments to procedures. Named arguments are especially useful when you have a procedure that takes a large number of optional arguments. Forcing the arguments to be in a particular order makes for awkward-looking function calls, and a fragile API. Keywords can also make code more readable, because every value supplied via keyword argument has a label right next to it that says what it is.

There are three simple guidelines to using keywords:

Keywords must be declared globally before they can be used.

Keywords must be initialized at the start of any procedure that uses them.

Keywords can be forwarded.

1.1. Declaring Keywords

Before you can use any keyword, it must be declared globally — technically in the scope in which it will be used, which will be Conkeror's application scope. This is because a keyword is actually a global variable. Keywords are declared with the function define_keywords. Define_keywords takes any number of strings that name these variables. In order to make the keyword plumbing work, a naming convention is needed, and we chose the rule that all keyword names must start with the dollar sign ($). A typical call to define_keywords looks something like this:

define_keywords("$foo", "$bar", "$baz");

Any given keyword only needs to be declared once, but it must be declared before it is used, so if you have several different procedures that use some of the same keywords, there only needs to be one call to define_keywords for them, but it must be earlier in the code than any use of those keywords.

1.2. Using Keyword Arguments in a Procedure

Having defined some keywords, we can now call procedures with them:

hello_world($foo = 1, $bar = 2, $baz = 3);

However, there must be another piece to this magic, because otherwise this would just be passing three positional arguments to hello_world, and making three inline assignments to the global variables $foo, $bar, and $baz. We may write a bit later on the internal details, but for now, let's focus on what hello_world needs to do to access the values that were passed to it via keywords. It needs a way to know that the value 3 was associated with $baz, and not some other keyword, for example.

In any function that uses keywords, the first thing to do is a special processing of the arguments that were passed to the function. This is done by calling keywords(arguments) as in the following example:

function hello_world () {
keywords(arguments);
}

The variable arguments is special in JavaScript. It is a reference, available in every function, to the arguments that were passed to that function. It is an array. The procedure keywords, which is provided by keywords.js, is designed to process this special arguments object. It loops through it, finds the values that were passed by keyword, and creates properties in it associating the keyword names to values. We can now use them like this: