Please check my understanding - AS, JXA, Javascript and lastly variables

Hi,

I have just started experimenting with Javascript and would appreciate a check of my understanding.

  1. Applescript is not being developed and as it does not run on finger painting devices (iOS) it is likely that it will be removed from MacOS at some point in the future.

  2. Applescript communicates with applications using Apple Events which are or are part of what is known as Open System Architecture or OSA.

  3. Apple now favour scripting using Javascript for Applications which is known as JXA. It was launched circa 2014 and described is as a peer to Applescript.

  4. JXA should not be confused with JSX which is something to do with XML.

  5. JXA communicates with non-Omni applications using Apple Events and OSA.

  6. Omni have developed their own javascript interpreter which is built into all their applications i.e. iOS apps as well.

  7. Is it correct to describe the Omni javascript as JXA. It appears to me that the OmniOutline dictionary displayed in Apple Script Editor is different to the commands described in the Omni-Automation tutorial e.g. I see Row but not item and no sign of Editors or Root. Should or does the Omni version have a different name e.g. Javascript for OmniAutomation JOA perhaps?

  8. I have an Applescript that copies the text of a selected email into the frontmost outline document. This means that the script has two tell statements, the first to Apple Mail the second to OmniOutliner. Am I correct in thinking that when I rewrite this script using Javascript the new script will have to be written in the version that is described in the Script Editor dictionary or is there a way of mixing the two? Perhaps by using the encoded URL option.

  9. How should I declare variables ? I ask because I have read that var can cause “namespace” issues and that let should generally be used. In an investigation of how to rewrite my Applescript I found the following JXA script :

'use strict';

var app = Application.currentApplication()
app.includeStandardAdditions = true

var Mail = Application('com.apple.mail')
    var selectedMails = Mail.selection();
				
    for (var aMail of selectedMails) {
        app.displayDialog(aMail.subject());
    }

This runs and can be run multiple times from script editor without any issues. Then I replaced all the var statements with let . It ran o.k. once but on the second run an error was raised to the effect that the variable was already defined. This is the opposite to what I was expecting which was that all variables should be destroyed at the end of the script i.e. the closing brace. Obviously I do not understand.

Lastly lets not even talk about semi-colons which are optional until they are not.

best wishes

Simon

More likely to gradually wilt and shrivel over a long sunset – not sure that there would be any particular motive for removing it. Each new macOS typically erodes it only fractionally at the sandboxing edges.

JXA is there, and very useful, though not fully implemented (location specifiers don’t work, necessitating occasional retreats to AppleScript). macOS Automation was already in slight disarray and decline at the time of its creation, and I don’t think it’s any more likely to see further updates than AppleScript. Both are in sunset now.

I don’t think there’s a basis for saying that Apple has any preference between the two.

JavaScript generally (considered separately from the particular issue of the JXA Automation library) clearly has a more solid future on Apple hardware than AppleScript:

  • it is actively developed in for use in Safari, in competition with Chrome etc
  • it’s cross-platform - you can run the same JS evaluation context (JSContext in Apple programming interfaces) on both macOS and the phone / pad iOS platforms.

That’s how Omni is able to support the same scripting interface on both iOS and macOS.

See, FWIW https://reactjs.org/docs/introducing-jsx.html, and yes, not relevant, essentially part of a library used with Web JavaScript.

No.

They both use the same JavaScript interpreter as Safari, but they are entirely separate instances of JSContext.

The Omni JSContext is implemented on iOS etc as well as on macOS.
The JXA JSContext trades in Apple Events and is macOS only.

The Omni JS evaluation context doesn’t have access to the JXA Automation library, and the Omni Application interfaces are not defined in the JXA evaluation context.

Var introduces subtle complexities and is now deprecated. There’s a discussion in Eloquent JavaScript.

const is the default for any name that you want to attach to a JS value or reference which won’t change at any stage of your script.

let is the fall-back if you are going to mutate the meaning of an assigned name during your script run.

(In scripting, as in math, avoiding mutations in the meaning of a name helps to keep things clear and simple, so it’s a good sign if you are using const. Most debugging arises from mutations, entailing head-scratching about what a particular name really meant at a particular moment. Prefer const to let, and forget about var)

You are executing that script in a densely populated global namespace which persists between script runs.

To avoid confusion, and to avoid pollutions of the global namespace, including unexpected effects on later script runs, you should always wrap a script in a temporary local namespace, provided by a pair of { … } braces.

Wrapped this way, in its own temporary name-space, your script can be run several times, without later runs being polluted by earlier runs.

e.g.

(() => {
    "use strict";

    const app = Application.currentApplication();

    app.includeStandardAdditions = true;

    const Mail = Application("com.apple.mail");
    const selectedMails = Mail.selection();

    for (const aMail of selectedMails) {
        app.displayDialog(aMail.subject());
    }
})();

See: IIFE – Immediately Invoked Function Expresstion –MDN Web Docs Glossary

and:

4 Likes

Each JS Scripting Context on an Apple platform (iOS etc or macOS) has two components:

  1. A self-contained (brower-independent) copy of the JavascriptCore JS interpreter (an instance of JSContext) which you can fire up, and
  2. In the global namespace of that JS interpreter instance, a particular set of library objects (different in the case of each scriptable application).

You can tell which one you are looking at by evaluating the this keyword at the JS command line, or as a one-word standalone script.

You will find that each Omni application has its own JS instance, and reveals a distinct (application-specific) set of library objects when you run the one-word this script:

  • OmniOutliner’s JS Context has an interface to OO documents in its JS context,
  • OmniPlan’s JS Context has a library of objects providing an interface to projects and tasks,
  • JXA’s global context contains a single library object called Automation which defines a bundle of methods and properties for Apple Event scripting,
  • TaskPaper’s JS Context has a set of library objects for interacting with TaskPaper documents
  • The Drafts JS Context’s global namespace contains a library for interacting with Drafts items

etc etc

JXA scripting is slower than scripting with OmniJS, TaskPaper JS, Drafts JS etc because it’s the only JSContext which is not embedded inside an application – it interacts with applications by sending and receiving Apple Events across a slightly sluggish interface – the osascript interface shared with AppleScript.

Given a choice between driving an Omni app:

  1. from the outside, using JXA, or
  2. from the inside, using the JSContext embedded inside the app itself,

you will always find that the app-embedded Omni JSContext runs faster.

3 Likes