Exit
  • Global community
    • Language:
      • Deutsch
      • English
      • Español
      • Français
      • Português
  • 日本語コミュニティ
  • 한국 커뮤니티
0

Javascript discussion: Variable scopes and functions

People's Champ ,
Feb 25, 2012 Feb 25, 2012

Hi,

I'm wondering how people proceed regarding variable scope, and calling

functions and things. For instance, it's finally sunk in that almost

always a variable should be declared with var to keep it local.

But along similar lines, how should one deal with functions? That is,

should every function be completely self contained -- i.e. must any

variables outside the scope of the function be passed to the function,

and the function may not alter any variables but those that are local to

it? Or is that not necessary to be so strict?

Functions also seem to be limited in that they can only return a single

variable. But what if I want to create a function that alters a bunch of

variables?

So I guess that's two questions:

(1) Should all functions be self-contained?

(2) What if the function needs to return a whole bunch of variables?

Thanks,

Ariel

TOPICS
Scripting
3.8K
Translate
Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
LEGEND ,
Feb 25, 2012 Feb 25, 2012

Ariel:

  A lot depends on what you are doing. What is the size of your project?

  Are you running in a persistent session? If you are running in #targetengine main, for instance, it hardly matters whether you use excessive global variables, because it is quite difficult for two scripts to badly interact.

  Remember that there are more than two scopes. It is not just "global" and "local." Javascript scope blocks are defined with functions, so, for instance, this example has four scopes:

var v1;

function f1() {

  var v2;

}

(function f2() {

  var v3;

  function f3() {

    var v4;

  }

})();

Each of v1, v2, v3, and v4 are in a different scope. There are four different scopes, though v2 and v3 are at the same level of hierarchy.

The really important key is to avoid the global scope, that is v1 (incidently, the name of the function f1 is also in the global scope, so that means two scripts in a persistent session that both have an f1() can collide.)

Whether f3 should be allowed to modify v3 (which is in scope) really depends. It depends on how likely f3 is to be reused outside of f2 in another piece of code, and how complicated f2 and f3 are, and how annoying it is to try to manage the situation.

But along similar lines, how should one deal with functions? That is,

should every function be completely self contained -- i.e. must any

variables outside the scope of the function be passed to the function,

and the function may not alter any variables but those that are local to

it? Or is that not necessary to be so strict?

It is not necessary to be so strict, no.

But those non-local variables ought not be global. They should be in some other scope.

Functions also seem to be limited in that they can only return a single

variable. But what if I want to create a function that alters a bunch of

variables?

Functions can easily return complex data structures. For instance:

function returnsArray() {

  return [1,2,3,4,5];

}

function returnsObject() {

  return {

    alpha: "Alpha is the first letter of the greek alphabet",

    bet: "A bet is a complex hedge against future events."

  };

}

I underlined the word alters in your text, because I wonder what you mean. It's probably not good style to alter out of scope variables if you can simply return copies of them.

Again, it depends. "What are you really trying to do?"

It is hard to talk about this stuff in the abstract. We need solid clear cases, and every case can be different.

Also, is anyone else going to read your code? 🙂

Hopefully this helps get you thinking.

Translate
Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
People's Champ ,
Feb 25, 2012 Feb 25, 2012

Hi John,

Thanks for chipping in. (Incidentally, I couldn't find any way of

marking answers correct when I visited the web forums a few days ago, so

I gave up.)

I find that I begin wondering about these things as soon as I've got

more than half a dozen functions, or a couple of hundred lines. What I

want is to write code that I can easily come back to a few months later

and make changes/add features -- so it's only me that sees the code, but

after long intervals it can almost be as though someone else has written

it! So I was wondering if I would be doing myself a favour by being more

strict about make functions independent etc. Also, I was noticing that

in the sample scripts that accompany InDesign (written by Olav Kvern?)

the functions seem always to require all variables to be passed to it.

"Remember that there are more than two scopes. It is not just "global"

and "local."

That only recently came to my attention. Perhaps I should read a book to

get some formal training about Javascript -- so far I'm relying on

picking things up as I go along (and of course us almost-middle-age

folks are likely to have had programming lessons at school, as well as

programming the old Commodore-64 and even Vic-20 at home -- which is

certainly my background in computing!) Anyway, I sunddenly realised

that, as you say, if a variable is declared with var, and then within

that scope a function is called, that variable is still accessible to

the function so long as the function does not create its own

similarly-labeled variable.

I'm not 100% sure what you mean by persistent session. Most of my

scripts are run once and then quit. However, some do create a modeless

dialog (ie where you can interface with the UI while running it), which

is the only time I need to use #targetengine.

But I think you've answered one of my questions when you say that the

thing to avoid is the "v1" scope. Although I don't really see what the

problem is in the context of InDesign scripting (unless someone else is

going to using your script as function in one of theirs). Probably in

Web design it's more of an issue, because a web page could be running

several scripts at the same time?

Regarding functions altering variables: for example, I have a catalog

script. myMasterPage is a variable that keeps track of which masterpage

is being used. A function addPage() will add a page, but will need to

update myMasterPage because many other functions in the script refer to

that. However, addPage() also needs to update the total page count

variable, the database-line-number-index-variable and several others,

which are all used in most other functions. It seems laborious and

unnecessary to pass them all to each function, then have the function

alter them and return an array that would then need to be deciphered and

applied back to the main variables. So in such a case I let the function

alter these "global" (though not v1) variables. You're saying that's okay.

However, the down-side is that intuitively I feel this makes the script

more "messy" -- less legible and professional. (On the other hand, I

recall reading that passing a lot of variables to functions comes with a

performance penalty.)

I knew a function could return an array. I'll have to read up on it

returing an object. (I mean, I guess I intuitively knew that too -- I'm

sure I've had functions return textFrames or what-have-you),

I'm just wondering what professional programmers do to keep scripts of

1000+ lines manageable.

Thanks,

Ariel

Translate
Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
LEGEND ,
Feb 25, 2012 Feb 25, 2012

Ariel:

(Incidentally, I couldn't find any way of  marking answers correct when I visited the web forums a few days ago, so I gave up.)

It's there...as long as you're logged in at least, and are the poster of the thread.

What I want is to write code that I can easily come back to a few months later

and make changes/add features -- so it's only me that sees the code, but

after long intervals it can almost be as though someone else has written

it! So I was wondering if I would be doing myself a favour by being more

strict about make functions independent etc. Also, I was noticing that

in the sample scripts that accompany InDesign (written by Olav Kvern?)

the functions seem always to require all variables to be passed to it.

Where it is not impractical to do so, you should make functions independent and reusable. But there are plenty of cases where it is impractical, or at least very annoying to do so.

The sample scripts for InDesign are written to be in parallel between three different languages, and have a certain lowest-common-denominator effect. They also make use of some practices I would consider Not Very Good. I would not recommend them as an example for how to learn to write a large Javascript project.

I'm not 100% sure what you mean by persistent session. Most of my

scripts are run once and then quit. However, some do create a modeless

dialog (ie where you can interface with the UI while running it), which

is the only time I need to use #targetengine.

Any script that specifies a #targetengine other than "main" is in a persistent session. It means that variables (and functions) will persist from script invokation to invokation. If you have two scripts that run in #targetengine session, for instance, because of their user interfaces, they can have conficting global variables. (Some people will suggest you should give each script its own #targetengine. I am not convinced this is a good idea, but my reasons against it are mostly speculation about performance and memory issues, which are things I will later tell you to not worry about.)

But I think you've answered one of my questions when you say that the

thing to avoid is the "v1" scope. Although I don't really see what the

problem is in the context of InDesign scripting (unless someone else is

going to using your script as function in one of theirs). Probably in

Web design it's more of an issue, because a web page could be running

several scripts at the same time?

It's more of an issue in web browsers, certainly (which I have ~no experience writing complex Javascript for, by the way), but it matters in ID, too. See above. It also complicates code reuse across projects.

Regarding functions altering variables: for example, I have a catalog

script. myMasterPage is a variable that keeps track of which masterpage

is being used. A function addPage() will add a page, but will need to

update myMasterPage because many other functions in the script refer to

that. However, addPage() also needs to update the total page count

variable, the database-line-number-index-variable and several others,

which are all used in most other functions. It seems laborious and

unnecessary to pass them all to each function, then have the function

alter them and return an array that would then need to be deciphered and

applied back to the main variables. So in such a case I let the function

alter these "global" (though not v1) variables. You're saying that's okay.

Yes, that is OK. It's not a good idea to call that scope "global," though, since you'll promote confusion. You could call it...outer function scope, maybe? Not sure; that assumes addPage() is nested within some other function whose scope is containing myMasterPage.

It is definitely true that you should not individually pass them to the function and return them as an array and reassign them to the outer function's variables.

I think it is OK for addPage() to change them, yes.

Another approach would be something like:

(function() {

  var MPstate = {

    totalPages: 0,

    dbline: -1

  };

  function addPage(state) {

    state.totalPages++;

    state.dbline=0;

    return state;

  }

  ...

  MPstate = addPage(MPstate);

})();

I don't think this is a particularly good approach, though. It is clunky and also doesn't permit an easy way for addPage() to return success or failure.

Of course it could instead do something like:

...

    return { success: true, state: state };

  }

  ...

  var returnVal = addPage(MPstate);

  if (returnVal.success) { MPstate = returnVal.state; }

...

but that's not very comforting either. Letting addPage() access it's parent functions variables works much much better, as you surmised.

However, the down-side is that intuitively I feel this makes the script

more "messy" -- less legible and professional. (On the other hand, I

recall reading that passing a lot of variables to functions comes with a

performance penalty.)

I think that as long as you sufficiently clearly comment your code it is fine.

Remember this sort of thing is part-and-parcel for a language that has classes and method functions inside those classes (e.g. Java, Python, ActionScript3, C++, etc.). It's totally reasonable for a class to define a bunch of variables that are scoped to that class and then implement a bunch of methods to modify those class variables. You should not sweat it.

Passing lots of variables to functions does not incur any meaningful performance penalty at the level you should be worrying about. Premature optimization is almost always a bad idea. On the other hand, you should avoid doing so for a different reason -- it is hard to read and confusing to remember when the number of arguments to something is more than three or so. For instance, compare:

addPage("iv", 3, "The rain in spain", 4, loremIpsumText);

addPage({ name: "iv", insertAfter: 3, headingText: "The rain in spain",

  numberColumns: 4, bodyText: loremIpsumText});

The latter is more verbose, but immensely more readable. And the order of parameters no longer matters.

You should, in general, use Objects in this way when the number of parameters exceeds about three.

I knew a function could return an array. I'll have to read up on it

returing an object. (I mean, I guess I intuitively knew that too -- I'm

sure I've had functions return textFrames or what-have-you),

Remember that in Javascript, when we say Object we mean something like an associative array or dictionary in other languages. An arbitrary set of name/value pairs. This is confusing because it also means other kinds of objects, like DOM objects (textFrames, etc.), which are technically Javascript Objects too, because everything inherits from Object.prototype. But...that's not what I mean.

So yes, read up on Objects. They are incredibly handy.

Translate
Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
People's Champ ,
Feb 25, 2012 Feb 25, 2012
Translate
Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
LEGEND ,
Feb 25, 2012 Feb 25, 2012

Your post was blank...

Translate
Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
People's Champ ,
Feb 25, 2012 Feb 25, 2012

Sorry It happens if I leave in the autoquote.

Translate
Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
LEGEND ,
Feb 25, 2012 Feb 25, 2012

Sorry It happens if I leave in the autoquote.

Yes...but what were you going to say?

Translate
Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
People's Champ ,
Feb 25, 2012 Feb 25, 2012

I was going to say the following, which I included in the previous

email. But I separated it from the apology with a line made up of

underscores. Looks like a line like that Jives things up and deletes all

subsequent text..... grr. (I miss the good old days when these forums

were accessible with NNTP).

Thanks John. Food for thought.

So I'm taking away from this that a well-commented script is allowed to

have functions that play with variables outside that function's scope --

although there is a preference for self-contained functions where

practical; and that using objects can be a good substitute for a

function that requires loads of parameters in a fixed order.

I have tried to create proper classes with functions and properties to

keep things simple in a long script, but they can also get unwieldly if

one doesn't think very carefully about what goes in the class and what

stays out.

I have to say that my admiration for InDesign's DOM rises -- I mean, how

on earth do they keep track of it all?!

Ariel

Translate
Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
LEGEND ,
Feb 25, 2012 Feb 25, 2012

But I separated it from the apology with a line made up of underscores.

Underscores? See http://forums.adobe.com/markuphelpfull.jspa for details, but underscores begin and terminate underlining. I'm not sure why they would cause the message to be eaten, but I suppose you could fiddle in the Testing forum...

So I'm taking away from this that a well-commented script is allowed to

have functions that play with variables outside that function's scope --

although there is a preference for self-contained functions where

practical; and that using objects can be a good substitute for a

function that requires loads of parameters in a fixed order.

"allowed"? Well, it all depends on your arbiter, these are rules of style.

I think you summarized me accurately, but I don't think you necessarily need to be well-commented to do such a thing, and you should always use good comments regardless!

I'm not sure what you mean when you say "proper classes" since, of course, Javascript doesn't have classes. you could mean many things.

The more time one spends looking at common design patterns for software, the easier it is to come up with natural organizations to address issues like this... but these kinds of software engineering issues rarely tend to arise with InDesign scripting, but most of it is just not that involved...

Edit: Use /markuphelpfull.jspa instead of the ancient JVD thing.

Translate
Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
People's Champ ,
Mar 12, 2012 Mar 12, 2012

It's there...as long as you're logged in at least, and are the poster of the thread.

John Hawkinson wrote:

Ariel:

(Incidentally, I couldn't find any way of  marking answers correct when I visited the web forums a few days ago, so I gave up.)

It's there...as long as you're logged in at least, and are the poster of the thread.

Can't see them. Strange. I'm using the web interface now, logged in, and poster of the thread. They're nowhere to be seen.

Translate
Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines
LEGEND ,
Mar 12, 2012 Mar 12, 2012
LATEST

Can't see them. Strange. I'm using the web interface now, logged in, and poster of the thread. They're nowhere to be seen.

Oh, I see. It is because, at topic creation time, this thread was not marked as a Question. You can tell from the icons in the index:

iconq.png

Translate
Report
Community guidelines
Be kind and respectful, give credit to the original source of content, and search for duplicates before posting. Learn more
community guidelines