does scope allow the transfer of non-global variables between functions?

L

lawrence

I'm learning Javascript. I downloaded a script for study. Please tell
me how the variable "loop" can have scope in the first function when
it is altered in the second function? It is not defined in global
space, therefore it is not a global variable, yes? Even if it was
global, how would it get from one function to another? In PHP
variables are copied by value. Are they copied by reference in
Javascript?


<SCRIPT LANGUAGE="JavaScript">
<!-- Original: Eddie Traversa ([email protected]) -->
<!-- Web Site: http://nirvana.media3.net -->

<!-- This script and many more are available free online at -->
<!-- The JavaScript Source!! http://javascript.internet.com -->

<!-- Begin

function verScroll(dir, spd, loop) {
loop = true;
direction = "up";
speed = 10;
scrolltimer = null;


if (document.layers) {
var page = eval(document.contentLayer);
} else {
if (document.getElementById) {
var page= eval("document.getElementById('contentLayer').style");
} else {
if (document.all) {
var page = eval(document.all.contentLayer.style);
}
}
}



direction = dir;
speed = parseInt(spd);
var y_pos = parseInt(page.top);

if (loop == true) {
if (direction == "dn") {
page.top = (y_pos - (speed));
} else {
if (direction == "up" && y_pos < 10) {
page.top = (y_pos + (speed));
} else {
if (direction == "top") {
page.top = 10;
}
}
}
scrolltimer = setTimeout("verScroll(direction,speed)", 1);
}
}


function stopScroll() {
loop = false;
clearTimeout(scrolltimer);
}


// End -->
</script>
 
M

Michael Winter

I'm learning Javascript. I downloaded a script for study.

Please don't "study" anything from Dynamic Drive and The JavaScript
Source. Most script repositories contain junk, and that observation
applies here, too.

Unfortunately, I don't have time to supply a better version of that script.
Please tell me how the variable "loop" can have scope in the first
function when it is altered in the second function? It is not defined in
global space, therefore it is not a global variable, yes?

You are correct that it isn't defined at a global level, but the variable
*is* global. When you declare a variable without the var keyword, that
variable automatically becomes global. That is why you should always use
var. Moreover, it's clear when a variable is being defined, rather than
simply modified.
Even if it was global, how would it get from one function to another?

Because it's global. A global variable is accessible to every function in
every script in the document or frame. That is why globals are bad: you
cannot properly control when they'll be modified, not only by your
scripts, but possibly by third-party scripts that use the same identifiers.
In PHP variables are copied by value. Are they copied by reference in
Javascript?

It depends on the type. Objects and functions are passed by reference, but
string, number, and boolean values[1] are passed by value.

Hope that helps,
Mike


[1] Also known as primatives. Note that there is a distinction between a
primitive value, and its corresponding object. When you use literals like
true, 'a string' or 421, you are specifying a value. When you need to use
a method like split(), that value is used to create a temporary object (if
you've used other OO languages, you'll be familiar with this process).
 
L

Lasse Reichstein Nielsen

I'm learning Javascript. I downloaded a script for study.

Bad idea :)
It's not a very good script, and not something you should learn
too much from, perhaps apart from what not to do. So, let's help
you with that :)
Please tell me how the variable "loop" can have scope in the first
function when it is altered in the second function? It is not
defined in global space, therefore it is not a global variable, yes?

Actually, in Javascript, a variable that isn't defined previously, and
is assigned to, will be created as a global variable (you still can't
read an undefined variable, only write to it).

However, in this case, "loop" is a *local* variable in the first
function, so the "loop" in the second function definitly isn't
the same variable.
Even if it was global, how would it get from one function to
another?

If it was global, they would both be referring to the same variable.
In PHP variables are copied by value. Are they copied by
reference in Javascript?

No. Arguments are passed by value.


So, comments on the script:
<SCRIPT LANGUAGE="JavaScript">

should be:
<!-- Original: Eddie Traversa ([email protected]) -->

I don't recommend using HTML comments inside a script tag. Browsers
accept it, buy it isn't Javascript. A Javascript comment is
/* this is a comment *.
or
// comment to end of line
<!-- Begin

This line is supposed to hide the script from browsers that don't
understand the "script" tag. Browser have understood the script
tag since Netscape 2. It isn't necessary any more.
function verScroll(dir, spd, loop) {
loop = true;

So, this function takes three arguments, but the value of the third is
immediately set to "true". Didn't he know how to declare a local variable
(that would be:
var loop = true;
but arguments are also local variables).
direction = "up";

Unless another function, outside the scope of this one, uses the
variable, it should be declared as local:
var direction = "up";
speed = 10;
scrolltimer = null;

ditto, ditto.
if (document.layers) {
var page = eval(document.contentLayer);

Here he checks whether the "document.layers" object exists. This is
supposed to identify a Netscape 4 browser (although at least one other
browser also has a non-null "document.layers" property).

After that, he uses "eval" to evaluate the string stored as the
property "document.contentLayer". However, I doubt that
"document.contentLayer" refers to a string. More likely it is a positioned,
named element, i.e., and object, so eval just returns that object
and does nothing.
} else {
if (document.getElementById) {
var page= eval("document.getElementById('contentLayer').style");

Then we test for the existence of "document.getElementById" and
actually try to use the detected method ... badly. The "eval" here
is completely unnecessary. Just do:

var page = document.getElementById('contentLayer').style;

At least the "eval" does something. It's just not a good way to do it. :)
} else {
if (document.all) {

Then we test for document.all, and if it exists, we use it.
var page = eval(document.all.contentLayer.style);

Again the call to "eval" does nothing and can be omitted.

direction = dir;

Now we overwrite the value of "direction" (set to "up" above, and not
used yet, so that initialization was wasted).
speed = parseInt(spd);

Then we convert "spd" to an integer. The function "parseInt" converts
its argument to a string, and then parses that string. If the argument
"spd" was already a number, using "Math.floor" would probably be better.
If the argument is a string, parseInt should be given a second argument,
the base for the number, to avoid, e.g., the string "024" being inter
preted as octal (giving 30, not 24). So:
speed = Math.floor(spd);
var y_pos = parseInt(page.top);
ditto.


if (loop == true) {

But "loop" *is* true, we set it to that in the first line of the
function.
It's not necessary to compare to a boolean in a test. The comparison
creates a boolean, but we already had one of those, so:
if (loop)
(if "loop" hadn't been true already :)
if (direction == "dn") {
page.top = (y_pos - (speed));

Here we assign a number value to "page.top". In most cases,
"page" is a style object, so what should be assigned is a
CSS length. It must end in a unit, e.g. "px". However, in
Netscape 4, it shouldn't. So, more tests would be necessary here,
but for standard compliant browsers, this should be:

page.top = (y_pos - speed) + "px";
} else {
if (direction == "up" && y_pos < 10) {
page.top = (y_pos + (speed));
ditto.

} else {
if (direction == "top") {
page.top = 10;
ditto.

}
}
}
scrolltimer = setTimeout("verScroll(direction,speed)", 1);

This is a reason for "direction" and "speed" to be global, because a
string as first argument to "setTimeout" is evaluated in the global
scope. For modern browsers (Netscape 4 and better), you can use a function
as first argument, which avoids this problem:
scrolltimer = setTimeout(function(){ verScroll(direction,speed); }, 1);

A second argument of "1" means a timeout of one millisecond. That might
not be smart :)
}
}


function stopScroll() {
loop = false;

So, this assigns to a global variable that isn't used anywhere else.
clearTimeout(scrolltimer);

This clears the timeout stored in the global variable "scrolltimer".
This actually makes sense :)

/L
 
R

Richard Cornford

lawrence said:
I'm learning Javascript. I downloaded a script for study.

Learning by studying other people's scripts is not a bad idea, but it
will be more productive in the long run if you can avoid studying poor
scripts. There are a number of tell-tale characteristics that can be
used to identify poor scripts, because they indicate that the script
author does not understand Javascript and/or HTML. The script you have
posted features most of them (as I will indicate below).
Please tell me how the variable "loop" can have scope in
the first function when it is altered in the second
function?

The variable - loop - is never declared (with the - var - keyword) and
as a result, when it is first created by an assignment of a value to the
identifier, it becomes a property of the global object and thus is
equivalent to a global variable.
It is not defined in global space, therefore
it is not a global variable, yes?
No.

Even if it was global,
how would it get from one function to another?

Global variables become named properties of the global object, and the
global object is the last object on the scope chain of every function
(and its execution context). Identifier resolution is carried out
against the scope chain of the current execution contexts so all
functions will resolve identifier that correspond with named properties
of the global object as references to the properties of that object
unless another object higher in the scope chain has a property with the
same name.
In PHP
variables are copied by value. Are they copied by
reference in Javascript?

Where any (ECMAScript) Object is concerned a variable will represent a
reference to that Object. Whether a variable that is assigned a
primitive value actually holds that value as such, or a reference to an
independent structure that holds the value, is unspecified and
unimportant.
<SCRIPT LANGUAGE="JavaScript">

To create valid HTML 4 a script element is required to have a TYPE
attribute. The type attribute renders the LANGUAGE attribute redundant
(and the Language attribute is officially deprecated and cannot be used
with the strict DTD). Competent authors of scripts for HTML web browsers
should be expected to possess a technical understanding of HTML which
will include an awareness of the requirements for valid mark-up.
<!-- Original: Eddie Traversa ([email protected]) -->

The ECMAScript specification defines two types of comment, a multi-line
comment indicated by being surrounded with - /* - and - */ -, and an
end-of-line comment indicated by starting with - // - and continuing to
the end of the current line of source code. A competent javascript
author should be expected to be using javascript style comments within
javascript source code.
<!-- Web Site: http://nirvana.media3.net -->

<!-- This script and many more are available free online at -->
<!-- The JavaScript Source!! http://javascript.internet.com -->

<!-- Begin

HTML style comments used to "hide scripts from older browsers" are no
longer needed as those 'older' browsers are two generations older than
the oldest browsers currently in use. Anyone using such a construct to
hid code from web browsers is either not up to date (by a long way), or
operating on the basis of an old wives' tale, neither of which should be
seen as an indicator of competence.
function verScroll(dir, spd, loop) {
loop = true;

Assigning a value to an undeclared identifier will result in a
correspondingly named property being created on the global object and
the value assigned to that property. Thus this assignment does create a
global variable.

Good code authoring style would suggest that if a variable is to be
global it should be explicitly declared as a global variable using the -
var - keyword in the global scope. Doing so avoids ambiguity and
prevents variables that are intended to be global from looking like an
authoring mistake.
direction = "up";
speed = 10;
scrolltimer = null;

if (document.layers) {
var page = eval(document.contentLayer);
} else {
if (document.getElementById) {
var page= eval("document.getElementById('contentLayer').style");
} else {
if (document.all) {
var page = eval(document.all.contentLayer.style);
<snip>

The use of the - eval - function is the most tell-tale indicator of an
incompetent javascript author. If - eval - is used to evaluate a
constructed dot-notation property accessor as a string, where a variable
is used to provide a single identifier in that property accessor, then
the - eval - use can be directly replaced with a bracket notation
property accessor and be 2 to 40 times more efficient and supported in
more web browser. Thus a competent javascript author will never eval a
dot-notation property acessor string.

However, in this case there is no variable part to the property accessor
string being used in the second branch of the if/else construct, and the
first and third branches are not even acting upon strings. When the
argument to - eval - is not a string the function just returns the value
of that argument. The author of this code is not only incompetent enough
to be using eval to resolve property accessors, but also does not
understand what eval does or how it works. This is programming by
mystical incantation.

The above construct can be replaced with:-

var page = null;
if(document.layers){
page = document.layers['contentLayer'];
}else if (document.getElementById){
page= document.getElementById('contentLayer').style;
}else if (document.all){
page = document.all.contentLayer.style;
}

- where the main practical difference is that the - eval - function is
just omitted.

The other difference being the change from - document.contentLayer -
to - document.layers['contentLayer'] - in the first branch. This is done
to correct the irrational logic in the original where a test for the
existence of the - document.layes - collection is followed by the
assumption that if the collection exists then the required layers will
also be a property of the - document -. This assumption is termed
'object inference' and is not valid. Having verified that the layers
collection exists on the current browser the only means of accessing the
layer that can be validly inferred from the test is through the -
document.layers - collection.

However, the code is still not really correct as it assumes that the
element reference retrieval will be successful, and that in the latter
two branches the retrieved element will possess a - style - property. It
would be better to split these two actions up into an initial attempt to
retreave the element reference (by whichever means is supported),
followed by verifiecation of the success of that action, and then
normalisaiton to the style object with code such as:-
var page = getElementWithId('contentLayer');//a function
//that uses layers,
//gEID or document.all
if(page){
page = page.style||page;
... // Assign style properties.
}

The only common indicator of poor javascript authoring missing from this
script is browser detection based on the properties of the - navigator -
object.

Richard.
 
M

Michael Winter

[snip]
Please tell me how the variable "loop" can have scope in the first
function when it is altered in the second function? It is not defined
in global space, therefore it is not a global variable, yes?

You are correct that it isn't defined at a global level, but the
variable *is* global.

After reading Lasse's post, that's obviously incorrect. I didn't see that
'loop' was an argument to the function and, therefore, local.

[snip]

Mike
 
L

lawrence

Lasse Reichstein Nielsen said:
Bad idea :)
It's not a very good script, and not something you should learn
too much from, perhaps apart from what not to do. So, let's help
you with that :)

Wow. But look at how you were able to tear it apart! I think I may
have learned more from this than I would have from the study of a well
done script. Sometimes you have to see the mistakes to know what to
avoid. Thanks for the analysis it was fun to read.



Actually, in Javascript, a variable that isn't defined previously, and
is assigned to, will be created as a global variable (you still can't
read an undefined variable, only write to it).

However, in this case, "loop" is a *local* variable in the first
function, so the "loop" in the second function definitly isn't
the same variable.

Automatic global variables in a language with pretentions to be OOP? I
find that surprising. I shouldn't judge it without knowing it better,
but so far I don't find the philosophy of Javascript especially
coherent.






So, this function takes three arguments, but the value of the third is
immediately set to "true". Didn't he know how to declare a local variable
(that would be:
var loop = true;
but arguments are also local variables).

I'm glad this made no sense to you. I first read it and was left
wondering if Javascript had some truly unique syntax that would allow
this bit to make sense.










Here he checks whether the "document.layers" object exists. This is
supposed to identify a Netscape 4 browser (although at least one other
browser also has a non-null "document.layers" property).

After that, he uses "eval" to evaluate the string stored as the
property "document.contentLayer". However, I doubt that
"document.contentLayer" refers to a string. More likely it is a positioned,
named element, i.e., and object, so eval just returns that object
and does nothing.


Then we test for the existence of "document.getElementById" and
actually try to use the detected method ... badly. The "eval" here
is completely unnecessary. Just do:

var page = document.getElementById('contentLayer').style;

At least the "eval" does something. It's just not a good way to do it. :)

But why do this at all? What do you think he was initially thinking?
Why would you even think to use eval() in this situation? To read the
value as a string? What would be the point? Or, to turn the question
around, in what situations would I want to run a returning value
through eval()????








Then we convert "spd" to an integer. The function "parseInt" converts
its argument to a string, and then parses that string. If the argument
"spd" was already a number, using "Math.floor" would probably be better.
If the argument is a string, parseInt should be given a second argument,
the base for the number, to avoid, e.g., the string "024" being inter
preted as octal (giving 30, not 24). So:
speed = Math.floor(spd);

I take it then you would use parseInt if you weren't sure your value
was an interger? If you were worried it was a string, could you cast
it to an interger and then use Math.floor()?????
 
T

Thomas 'PointedEars' Lahn

lawrence said:
Lasse Reichstein Nielsen [...] wrote [...]:
Actually, in Javascript, a variable that isn't defined previously, and
is assigned to, will be created as a global variable (you still can't
read an undefined variable, only write to it).

However, in this case, "loop" is a *local* variable in the first
function, so the "loop" in the second function definitly isn't
the same variable.

Automatic global variables in a language with pretentions to be OOP?

It *is* OOP, but as far as widely spread implementations are
concerned, not a class-based OOP, but a prototype-based one.
I find that surprising. I shouldn't judge it without knowing it better,

Yes, indeed you should not.
but so far I don't find the philosophy of Javascript especially
coherent.

Global variables are implemented as properties of the global object.

BTW: You really want to trim your quotes.


PointedEars
 
L

lawrence

Richard Cornford said:
Learning by studying other people's scripts is not a bad idea, but it
will be more productive in the long run if you can avoid studying poor
scripts. There are a number of tell-tale characteristics that can be
used to identify poor scripts, because they indicate that the script
author does not understand Javascript and/or HTML. The script you have
posted features most of them (as I will indicate below).

Thanks. I'm sure in a month or two I'll be able to see what's a bad
script. Still, this one was very instructive. I think I learned alot
by seeing this one torn down.



Global variables become named properties of the global object, and the
global object is the last object on the scope chain of every function
(and its execution context). Identifier resolution is carried out
against the scope chain of the current execution contexts so all
functions will resolve identifier that correspond with named properties
of the global object as references to the properties of that object
unless another object higher in the scope chain has a property with the
same name.

Your explanation makes a great deal clear to me. I was wondering how a
variable automatically became global in a supposedly OOP language. To
say that it automatically becomes a property of the global object
helps me understand the reasoning that guided the language designers.







Where any (ECMAScript) Object is concerned a variable will represent a
reference to that Object. Whether a variable that is assigned a
primitive value actually holds that value as such, or a reference to an
independent structure that holds the value, is unspecified and
unimportant.

That's an interesting point. I suppose that Javascript is abstract
enough that one never faces a situation where the difference between
the reference and the value of the reference makes a difference?












Good code authoring style would suggest that if a variable is to be
global it should be explicitly declared as a global variable using the -
var - keyword in the global scope. Doing so avoids ambiguity and
prevents variables that are intended to be global from looking like an
authoring mistake.

Good point.





<snip>

The use of the - eval - function is the most tell-tale indicator of an
incompetent javascript author. If - eval - is used to evaluate a
constructed dot-notation property accessor as a string, where a variable
is used to provide a single identifier in that property accessor, then
the - eval - use can be directly replaced with a bracket notation
property accessor and be 2 to 40 times more efficient and supported in
more web browser. Thus a competent javascript author will never eval a
dot-notation property acessor string.

So he was using eval() to get the statement to resolve as a string?
I'm surprised Javascript doesn't do that automatically. What is the
bracket notation? With what little I know, I should think for most
modern browsers the document.getElementById() should work, and would
work without eval(), yes?





However, in this case there is no variable part to the property accessor
string being used in the second branch of the if/else construct, and the
first and third branches are not even acting upon strings.

I think by this you mean that eval is completely redundant for this
line:

eval(document.all.contentLayer.style);







var page = null;
if(document.layers){
page = document.layers['contentLayer'];
}else if (document.getElementById){
page= document.getElementById('contentLayer').style;
}else if (document.all){
page = document.all.contentLayer.style;
}

- where the main practical difference is that the - eval - function is
just omitted.

Your code makes much more sense that the original.







The other difference being the change from - document.contentLayer -
to - document.layers['contentLayer'] - in the first branch. This is done
to correct the irrational logic in the original where a test for the
existence of the - document.layes - collection is followed by the
assumption that if the collection exists then the required layers will
also be a property of the - document -. This assumption is termed
'object inference' and is not valid. Having verified that the layers
collection exists on the current browser the only means of accessing the
layer that can be validly inferred from the test is through the -
document.layers - collection.

Good points. You're basically saying that it is idiotic to test for
this:

and then if true simply assume this:
However, the code is still not really correct as it assumes that the
element reference retrieval will be successful, and that in the latter
two branches the retrieved element will possess a - style - property. It
would be better to split these two actions up into an initial attempt to
retreave the element reference (by whichever means is supported),
followed by verifiecation of the success of that action, and then
normalisaiton to the style object with code such as:-
var page = getElementWithId('contentLayer');//a function
//that uses layers,
//gEID or document.all
if(page){
page = page.style||page;
... // Assign style properties.
}

The only common indicator of poor javascript authoring missing from this
script is browser detection based on the properties of the - navigator -
object.

Thanks for the thorough review. It's been very helpful.
 
R

Richard Cornford

lawrence said:
Richard Cornford wrote:
... . I'm sure in a month or two I'll be able to see
what's a bad script. Still, this one was very instructive.
I think I learned alot by seeing this one torn down.

That might prove an optimistic expectation. Cross-browser scripting is
not at all easy, it takes quite a lot of study just to become familiar
with the issues, let alone learn or devise and implement strategies for
handling them. Be cautious of any impression of confidence that follows
form superficial early success, there are not that many people who are
really good a javascript (certainly less than believe themselves good at
it) and it takes work to become one of them.

Your explanation makes a great deal clear to me. I was
wondering how a variable automatically became global in
a supposedly OOP language. To say that it automatically
becomes a property of the global object helps me
understand the reasoning that guided the language designers.

It would probably be limiting to be thinking of javascript as an
exclusively OO language. Having first class functions and closures
allows functional programming in javascript. Though by specification
ECMAScirpt is amenable to being conceived entirely as structures of
interrelated objects (and that is how I tend to conceive it).

That's an interesting point. I suppose that Javascript is
abstract enough that one never faces a situation where the
difference between the reference and the value of the
reference makes a difference?

Yes, at least the specification is such that it could be satisfied by
many different styles of implementation, and the true nature of
references and values is only of relevance to the implementation.

So he was using eval() to get the statement to resolve as a string?

eval, given a string argument (only), executes that string as an
ECMAScirpt Program, returning the result of that program. That result is
effectively the value of the last statement executed within that program
(assuming no exceptions). And in the case of the original code (where
the input was in the form of a string) that program string consisted of
a single statement made up of a single MemberExpression or
CallExpression.

However, whatever the actual intention of the original author was is
difficult to say as he clearly had little understanding of how eval
works. So it is as likely that it was pure coincidence if the eventual
code did (or at least superficially appeared to do) what was intended.
I'm surprised Javascript doesn't do that automatically.

Treat strings as source code? (chaos)
What is the bracket notation?

Until you make the effort to become familiar with the contents of the
FAQ you will find yourself repeatedly referred to it:-

With what little I know, I should think for most
modern browsers the document.getElementById() should work,
and would work without eval(), yes?

There is almost nothing that cannot be done without eval, and nothing
that you are likely to want to do.

However, nothing in a web browser environment (in an Internet context)
should be expected to work. Javascript calls for a particularly
defensive and cautions style of authoring, with planned (and controlled)
clean degradation in the face of any lack of browser support for any
feature.

I think by this you mean that eval is completely
redundant for this line:

eval(document.all.contentLayer.style);

Yes, unless the - style - property of the object referred to by -
document.all.contentLayer - is a string (which would not be expected in
a browser environment, especially one supporting the document.all
collection) then eval will just return the value provided as its
argument. The eval call is pointless, redundant and wasteful.

Good points. You're basically saying that it is idiotic to
test for this:


and then if true simply assume this:
<snip>

Yes, feature detecting should be done in a way such that the tests used
have a direct one-to-one relationship with the code that is guarded by
them (wherever that is possible, and it usually is).

Richard.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,797
Messages
2,569,647
Members
45,377
Latest member
Zebacus

Latest Threads

Top