Hi @orib7317974, To complete Brian's excellent summary: I. On Lexical Scoping in ExtendScript • A script runs within a specific engine that somehow defines a namespace, but that's an informal designation—memspace would probably be much more appropriate—because engines do not work together. Every script runs in its own engine, which by default is non-persistent and has the name "main", as reported by $.engineName. You can also set up a persistent engine using the directive #targetengine "myEngineName". Then your script can recover, from one execution to another, the lexical scope, assigned data and references as you left them. That's the most popular way to save/restore data or settings within an InDesign session. Persistent does not mean permanent. (We sometimes use the term session-persistent to clarify, by contrast with a few application-persistent entities that can still be scoped at a higher level: see the menu structure(s), or methods like app.insertLabel/extractLabel, $.setenv/getenv, etc.) • Given the working engine of your script, assume that anything declared or done outside of an explicit function body is somehow processed from an implicit, global, unnamed function that encompasses the entire program. (The jsxbin encoding shows that things are handled exactly that way.) That hidden function determines the top-level lexical scope, which is often referred to as the [[global]] scope and can be (partially) dissected from the $.global object. • In concrete terms, any identifier declared and/or assigned in the [[global] scope is to be referenced in the $.global object, but there are a few syntactical rules to assimilate well. (By the way, those rules work the same in lower lexical levels.) The most important one (which you already know for sure) is that every assignment or use of an undeclared identifier involves the [[global]] scope. Hence you need const… or var… to specifically apply a scope restriction to the current function body (including inner function(s) when a closure is involved). But another crucial rule is this: the line var x = 123; represents two instructions that are completely separated in space. What the interpreter will see, in fact, is: var x; [at the top of the function body, declaring the identifier in the lexical scope and presetting its value to undefined] x = 123; [at the exact location where the line “var x = 123;” is written, which then performs the assignment] • As a result, when you're writing code in the [[global]] scope, there is a very fine distinction between var x = 123; and x = 123; In both cases the variable will indeed exist [[globally]]. However, it is “known to exist” at any point of your code only in the first case, because the “var x;” declaration is implicitly read prior to any assignment, making $.global.x already set to undefined. So the following code will not throw an error (despite the post-assignment): alert( myVar ); // => undefined var myVar = 123; while this code will fail: alert( myVar ); // => RUNTIME ERROR: "myVar is undefined" myVar = 123; You could check as well that $.global.hasOwnProperty('x') is true in the first case, false in the 2nd case. Of course these examples are stupid, because their brevity makes them appear stupid. But in a more complex program with nested functions and so, this may explain why something like (function testVar(){ alert(myVar) })() can produce an error whose origin no longer appears with the same obviousness. • Along the same lines, a function declaration (as opposed to a function expression) is implicitly registered and processed at the beginning of the scope. To quote the ECMAScript standard, “at the top level of a function, or script, function declarations are treated like var declarations rather than like lexical declarations”.That's the reason why this works, myHello(); // OK function myHello(){ alert("hello") } while that does not: myHello(); // RUNTIME ERROR: myHello is not a function var myHello = function(){ alert("hello") }; The second case—throwing a runtime error—is interesting to analyze. As I explained, the identifier myHello is indeed declared upstream (in the scope), but still set to undefined when the statement myHello(); is encountered. And the fact that the (anonymous) function to be assigned (line 2) does not exist yet comes from the syntax “…=function(){…}” which forcibly introduces a function expression by now way enjoying the attributes of a function declaration. → These aspects are extensively documented in the ECMA-262 specification (3rd edition); ExtendScript—for once—respects those rules from start to finish. II. About Anonymous Functions • An anonymous function is, in all and for all, a Function instance whose name property has been automatically set to "anonymous" because of the functional expression which it emanates from. Although this characterizes function expressions over function declarations (that syntactically require a name), this is not connected in itself to auto-execution, closure, and similar phenomena. • Moreover, naming a function is always possible—except from calling the Function contructor—and sometimes useful even with function expressions. Compare: ( function(){ alert(callee.name) } )(); // => 'anonymous' and ( function myFunc(){ alert(callee.name) } )(); // => 'myFunc' • The function name is a read-only property of the Function object and, when it's not "anonymous", it also becomes an identifier in the particular scope attached to that very function. Anyway, an anonymous function can still refers to itself using the callee reference. • I think that the most common confusion in this field is the mixture that some developers make between the name of a function (which locally becomes an identifier) and the possible reference(s) to that object (via other identifiers in the outer scope). In const myRef = function myFunc(){…}; or $.myProp = function myFunc(){…}; myRef and $.myProp bear external references to a function whose internal name is “myFunc” (not “myRef” or “MyProp”.) • One could simply note that in the case of a regular function declaration, the name property and the lexical identifier seem to play the same role, though they are in fact of quite a different nature. In the body of a named function, its name-as-identifier most certainly takes precedence over any externally-scoped identifier, but we simply cannot test this hypothesis in the case of a regularly declared function [see next comment] function myFunc(){ alert(myFunc.arity) } // Here ‘myFunc’ a (very likely) handled as a local identifier myFunc(); // Here ‘myFunc’ is necessarily an identifier of the outer scope, including [[global]] Note. — A good reason to think that this assumption is correct, although unverifiable, is that in case of ambiguity local identifiers take precedence over external identifiers. Best, Marc
... View more