Why isn't my javascript working

So, at first I thought it was an issue with the "let" and "var" keywords, because

iframe = document.querySelector('iframe');
console.log(iframe)

works, but

let iframe = document.querySelector('iframe');
console.log(iframe)

doesn't.

However,

let someVar = 'something'
console.log(someVar)

DOES work, which really starts messing with my head.

Tried running from a file and directly from KM, but with the same result.

What's the context ?

"works <-> doesn't"

What, if any, are the error messages ?
What kind of value are you trying to obtain ?

I think you would have to post a full KM example to make it clear what is really being evaluated, and in what interpreter and browser context.


In the meanwhile, you will get more helpful error messages, and a temporary local namespace, rather than a global one which persists between script runs, if you enclose your code in a "use strict" IIFE context.

If you have bound the name iFrame with let or const in a global namespace in one script run, then it will remain defined (unavailable for redefinition) in the next script run.

Polluting the global namespace is never a good plan.


(() => {
    "use strict";

    // Code to evaluate here, in the temporary namespace
    // roped off by an Immediately Invoked Function Expression
    // ...
})()

See:

Hey, sorry, been a bit confused and still struggling to figure out what the issue is.

I can run this

myVar = 'something';
console.log(myVar);

over and over again without any errors.

(p.s. for some reason it won't let me upload images here, am I missing something?)

The issue with my script might be some fuckery with the iframe element I am dealing with (I can't scrape the site until I right click and inspect element first in Chrome—very odd).

So, instead, I am running the script as a snippet.

HOWEVER, I am having a really hard time figuring out how to know when the script is done running.

Using your advice and that from Google (on where the global namespace is), I've tried adding a variable doneScraping to my (snippet) script that I could then check from KM.

However, this doesn't work.

I have tried doneScraping = true;, this.doneScraping = true;, and window.doneScraping = true;—all of which are then accessible from my Chrome console, but none of which can be used from KM—how can I do this?

The misleading part there is not using a let or const declaration.

or a "use strict" statement.

var declarations, including the implicit form you use above, allow for global name-space pollution and unconstrained re-write without letting you know what's happening.

I refer you, again, to the link in my previous post.

(The behaviour of the old var is complex, and prone to create the kind of confusion which you report. Its use is now deprecated. For discussion of the specifics, you could look at Eloquent JavaScript)

These questions are not, however, specific to the Keyboard Maestro execution context.


It may help you to makes sense of, and adopt, the following:

Strict mode - JavaScript | MDN

Sloppy mode (myVar = 'something') is just not worth the confusion and time-wasting which it inevitably entails.

Compare the results of evaluating a strict mode version in which a name binding is properly made,

(() => {
    "use strict";

    const myVar = "something";

    console.log(myVar);

    return myVar;
})();

with an attempt at sloppy syntax (failing to make a let or const name binding)

(() => {
    "use strict";

    myVar = "something";

    console.log(myVar);

    return myVar;
})();
ReferenceError: Can't find variable: myVar 

Strict mode is much more helpful and eloquent. It lets you know what's going on.

and to concretise the problems arising in JavaScript embeddings like those in browsers, and Apple's Script Editor, in which the global script environment (including any variables declared in the global namespace) persists between different script runs:

Try evaluating this in Script Editor (the language selector at top left set to JavaScript, rather than AppleScript)

let myVar = 'something'

This works OK first time. But now hit Run again:

Error: SyntaxError: Can't create duplicate variable: 'myVar'

The name myVar was still sitting there, polluting the global namespace, and you have already declared it.


If, however, you create a temporary and local name space of your own (IIFE), instead of polluting the global namespace as above, you can run the code as often as you like, even with const rather than let:

(() => {
    "use strict";

    const myVar = "something";

    console.log(myVar);

    return myVar;
})();

To contrast the three ways of binding a name to a value (whether variable or constant):

  • var – complex behaviour, deprecated, lets you repeatedly pollute and re-pollute the global namespace without letting you know that that is what you are doing.
  • let – better: mutable, but not redeclarable – you soon learn about global namespace pollution.
  • const – best, for most purposes: not only non-redeclarable, but also non-mutating. Mutation is the source of most head-scratching and wasted time – a name should ideally have a fixed value – restricting mutation to the cases where you really need it will save you time. (const also happens, though this is always irrelevant, to provide the fastest-performing kind of name binding in JS)

Summary:

  • DO use IIFE and "use strict", const and (if necessary, let)
  • DON'T use var, either explicitly (var myVar =) or implicitly (myVar = )

To stick with const (and avoid needing let for loops), let the interpreter do the house-work and use the pre-built .map .flatMap .filter and .reduce instead of constructing your own buggy loops again and again.


Why not pollute the global namespace ? (It's crowded - and persists across runs).kmmacros (2.5 KB)