Comparing JavaScript for Automation (JXA) and AppleScript

That's right – JavaScript interpreter environments, like Script Editor, and the JavaScript console in browsers, generally have a persistent global name space, containing a whole lot of definitions. It's a crowded space.

You can see its contents by running the following one-word JS script in Script Editor:

this

(You will see a lot of crowded detail appearing in the Script Editor Results panel)

If you don't write your code definitions inside a function wrapping, they are just added to this already cluttered global namespace, where they stay, even after the script has run, creating hidden pollution and sources of confusion downstream.

If we try to skip an outer function-wrapping, and run the following bare script in Script Editor, it will work the first time, but fail if we hit the Run button again:

const n = 2;

n + n;

If, however, we enclose our code in an immediately invoked function expression (IIFE), then:

  • it has a private and temporary name-space of its own, between { the braces },
  • the global name-space is protected from pollution,
  • and the script succeeds with every re-run:
(() => {
    "use strict";

    const n = 2;

    return n + n;
})();

Breaking it down, an expression like:

() => 2

defines an un-named function which just returns a value,

adding braces gives it its own name-space, in which we can use const and let to bind names to values:

() => {
    const n = 2;

    return n + n;
}

Adding "use strict"; switches on more helpful error messages if things go wrong:

() => {
    "use strict";

    const n = 2;

    return n + n;
}

and wrapping it in parentheses, with a final () for invocation/running

turns it into a function expression which is "immediately invoked".

(without that, it will just be a function definition which isn't actually run)

IIFE:

(() => {
    "use strict";

    const n = 2;

    return n + n;
})();

As an alternative to an IIFE, which works in any JS interpreter, the special environment of osascript and "Script Editor" also pre-defines the special name run, which you can use as an alternative top-level function wrapping.

function run() {
    "use strict";

    const n = 2;

    return n + n;
};

This run function approach won't, however, work in other scripting contexts, like OmniGroup omniJS, the TaskPaper JSContext, Drafts, Scriptable, or web scripting.

(The advantage of an IIFE is that it's available everywhere).

I personally have an empty IIFE bound to a shortcut for quick pasting.

14 Likes