Stylistic concerns for large application

C

Chris Smith

'Morning,

Within the next few months, I'm going to embark upon a comparatively
rather large base of JavaScript code to be called from a web browser
environment. Not too awfully huge, but probably between 10K and 15K
LoC. Having seen only the kinds of scripts that support interactive web
pages, I'm dreading trying to stretch JavaScript that far. Has anyone
here done something like this, and want to share some experience?

In my feeble experience, I've adopted a few stylistic tendencies that I
wanted to run by those of you with more experience, to see if you
foresee problems. Here are a few questions:

1. JavaScript books I've seen all seem to define object methods as
functions with a global name, and then assign them into an object as a
function. I'm concerned that this leads to a lot of global names. I've
been looking at using anonymous functions as an alternative, as in:

/* constructor */
function Foo()
{
/* methods */

this.foo = function()
{
...
}

this.bar = function(baz)
{
...
}

/* fields */
this.kazoo = this.foo();
this.kazam = null;
}

That way, I only define one global name, and don't end up with ugly
temporary names that only exist to be used in assignments for object
methods.

2. What do you normally do with methods that are only intended for
internal use within an object, and not to be called from the outside
world? Do you tag them somehow (I've been looking at a leading
underscore... I generally dislike these kinds of identifier tags, but in
the absence of language support for visibility, I might be convinced to
adopt one), or merely provide separate documentation of publis
interfaces for each object and don't include them there?

3. How do I combine lots of JavaScript source files into a single web
page. Having zillions of script tags seems hokey, but I can't find a
way to include one JavaScript source file from another, so it looks like
everything does need to be included directly from the HTML source. Any
other solution? Does this mean that the page will wait on the loading
of this large amount of JavaScript before it finished its own loading?

I'm considering solutions like having zero-size frames in a frameset
that load other pages with JavaScript code in them, to avoid holding up
the loading of functional pages. Any other ideas?

--
www.designacourse.com
The Easiest Way to Train Anyone... Anywhere.

Chris Smith - Lead Software Developer/Technical Trainer
MindIQ Corporation
 
R

Richard Cornford

... . I've been looking at using anonymous
functions as an alternative, as in:

/* constructor */
function Foo()
{
/* methods */
this.foo = function()
{
...
}
this.bar = function(baz)
{
...
}
/* fields */
this.kazoo = this.foo();
this.kazam = null;
}

Alternatively:-

/* constructor */
function Foo()
{
/* fields */
this.kazoo = this.foo();
this.kazam = null;
}
Foo.prototype.foo = function(){
...
}
Foo.prototype.bar = function(){
...
}

- the functions assigned to the prototype become methods of the object.
They do not create named functions in the global namespace and only one
function object is created (rather than one function object for each
inner function assigned as a method from within the constructor for each
invocation of the constructor) for each method.

2. What do you normally do with methods that are only intended for
internal use within an object, and not to be called from the outside
world?

Make them private members:-

<URL: http://www.crockford.com/javascript/private.html >

(also use groups.google.com to locate a clj thread with the subject
"closures, what are they good for?" for privat static members).
(and also locate and read the page on inheritance on www.crockford.com).

but in the absence of language support for visibility,
<snip>

The language has supported visibility (though not explicitly) since
version 1.1.

3. How do I combine lots of JavaScript source files into a
single web page.

Depends on the specific requirement but document.write can be used by
one script to write one or more SCRIPT tags into a page with their SRC
attributes set to appropriate files (my experience is that this is most
reliable if it is the last operation within a particular script section
or file). Other dynamic JS file loading techniques are not very
cross-browser.

Does this mean that the page will wait on the loading of this
large amount of JavaScript before it finished its own loading?

Mostly yes, but you can set a flag to false at the top of a file and
true at the bottom and not use the contents of that file until the value
of the flag is true.
I'm considering solutions like having zero-size frames in a
frameset that load other pages with JavaScript ...
<snip>

I tried that a couple of years ago and decided that it was not worth the
effort. Frames introduce lots of issues, both with scripting and in
general. Given the choice (and any server-side scripting/include
support) I would not now go anywhere near framesets. If you set your JS
files to cache properly on the client you should not suffer for their
size more than once.

Richard.
 
L

Lasse Reichstein Nielsen

Chris Smith said:
Okay, fair enough, but I'm beginning to see that this answer interacts
with what you say about hiding members... if I define methods in the
manner you describe above, then they don't have access to any more of
the object than an external observer would.

Javascript objects are completely transparent. There is no more to
access than what an external observer can see.

If you add the so-called "private members" then it is not really
supported by Javascript objects, but it can be *emulated* by scoped
variables shared by the methods.
That's not really consistent with a set of reasonable visibility
rules (that is, I never really want to hide an object's state from
itself),

You cannot hide anything from anybody using only Javascript objects.
so the only reason I'd define functions in the prototype is
the performance gain from only defining the function once rather
than per-object?

It is. There is no noticable difference between
---
function Foo(){}
Foo.prototype.bar = function(x){this.last=x;alert(x)};
---
and
---
function Foo(){
this.bar = function(x){this.last=x;alert(x);}
}
---
when you write "new Foo()". The first have better memory usage
if you create more objects.
Hmm. Can you point to some "other" techniques?

In some browsers, changing the "src" property of a script element will
load the code. In others it won't.
In some browsers, creating a new script element with DOM methods will
load the code. in others it won't.
My concern is that if I have a page with thirty-five to fourty
'script type="text/javascript" src="..."' lines at the top, that the
browser won't finish loading and rendering the page until it makes
all thirty-five to fourty HTTP requests for the source files, parses
them, and finishes running them. After all, what if they contained a
document.write somewhere?

You can add the "defer" attribute to the script tags:
<script type="text/javascript" src="foo.js" defer="defer"></script>
All that means is that you promise that the source contains no
document.write's, and that parsing can continue without waiting for the
code to be loaded and run.

/L
 
D

Dr John Stockton

JRS: In article <[email protected]>, seen in
news:comp.lang.javascript said:
3. How do I combine lots of JavaScript source files into a single web
page. Having zillions of script tags seems hokey, but I can't find a
way to include one JavaScript source file from another, so it looks like
everything does need to be included directly from the HTML source.

If you had looked in the regularly -posted and -cited FAQ, you would
have found no relevant occurrence of "include", which seems a pity.

Most of my javascript date pages use include files; but start at
<URL:http://www.merlyn.demon.co.uk/js-index.htm#IF>, visit
<URL:http://www.merlyn.demon.co.uk/js-nclds.htm> and
<URL:http://www.merlyn.demon.co.uk/js-other.htm>.

<FAQENTRY> - something on include files?

And perhaps the FAQ could link to an associated demonstration page,
intended to be most illustrative under View Source??

Might FAQ 3.2 be more readable with <ul> ... <li> ... </ul> ?

</FAQENTRY>

I think an included file can include another by document.write of the
HTML that calls the next; but I've not done it.

Code in an include file can use code in a previous include file, IIRC.

If you include a given file more than once, you might get confused but
the browser should do what you are actually asking for.
 
R

Richard Cornford

Okay, fair enough, but I'm beginning to see that this answer
interacts with what you say about hiding members... if I
define methods in the manner you describe above, then they
don't have access to any more of the object than an external
observer would. That's not really consistent with a set of
reasonable visibility rules (that is, I never really want to hide
an object's state from itself), so the only reason I'd define
functions in the prototype is the performance gain from only
defining the function once rather than per-object?

Functions assigned to properties of the prototype will not have access
to private members of their objects (though they could still access
private static members) but in practice no all object methods will need
access to an object's private members. They will have access to all of
the public methods of their own object instance so they will be able to
call "privileged" methods that can interact with private members. It is
a matter of deciding which methods to define in which context to achieve
the member access that is needed and get the best compromise between
performance, resource use and the desired level of encapsulation.

Whichever approach you use to defining a specific method you will still
be avoiding filling the global namespace with named function
definitions.

Hmm. Can you point to some "other" techniques?


Not sure what that means. My concern is that if I have a page with
thirty-five to fourty 'script type="text/javascript" src="..."' lines at
the top, that the browser won't finish loading and rendering the page
until it makes all thirty-five to fourty HTTP requests for the source
files, parses them, and finishes running them. After all, what if they
contained a document.write somewhere? The browser doesn't
know that they just define code that's called separately by the
browser in response to events. My feeling is that this could take
thirty seconds on a slow day before the page even renders.

An ideal solution would be to actually locate and load the code on
demand as it is actually used, but I'm giving up on that one, I think.
Nevertheless, I'd like to have it load in the background, and then
call it later.

<quote cite="http://www.w3.org/TR/html401/interact/scripts.html">
defer [CI]
When set, this boolean attribute provides a hint to the user agent
that the script is not going to generate any document content
(e.g., no "document.write" in javascript) and thus, the user agent
can continue parsing and rendering.
</quote>

That's actually not an issue, as framesets are already an important
part of the application. Not my choice... the specification I'm
implementing says that an object starts out by walking up the
frame structurse looking for a frame that defines a JavaScript
method with a particular name... so the page that's being loaded
MUST have a parent frame, at the very least.

Surely generalised code should check the current window for the function
name prior to chaining up through the parents? (so that it can work
without a frameset as well as with one).

Incidentally, if you have a frameset the frameset page is normally
fairly constant and you can load script into it so they would not need
to be re-loaded when any frame contents changed.

But do simultaneously test in a good range of browsers.
Wouldn't they be parsed each time? I'd think that parsing and walking
through 10K LoC would be non-trivial.

10K is really not that big and parsing is quick compared to downloading.

Richard.
 
R

Richard Cornford

I couldn't find one where that worked, maybe I am doing something
wrong. I seem to remember having heard that it should work.

IE 5+ but there is no point in using it as the createElement method
works OK there as well.

var script = document.createElement("script");
script.src = "../../alerter.js";
document.body.appendChild(script);
<snip>

Which Opera 7 version? When I tried this a couple of months back I could
not get it to work in Opera 7 up to version 7.02 (I think). It would be
nice if they have fixed it because then dynamic DOM level 2 support
might be taken to imply the ability to dynamically load scripts.

Richard.
 
R

Richard Cornford

It is. There is no noticable difference between
<snip>

The first should also execute quicker. Maybe not noticeably quicker but
almost certainly measurably quicker.

Richard.
 
L

Lasse Reichstein Nielsen

Richard Cornford said:
Which Opera 7 version?

Opera 7.20 beta 2.
When I tried this a couple of months back I could
not get it to work in Opera 7 up to version 7.02 (I think).

I checked, and it doesn't work in the last official
release, O7.11.

I admit I was a little surpriced that it worked, as I thought I
remembered that it shouldn't.

/L
 
L

Lasse Reichstein Nielsen

Richard Cornford said:
The first should also execute quicker. Maybe not noticeably quicker but
almost certainly measurably quicker.

Argh! Yes! I meant to say "apart from efficiency", but I somehow
forgot to write it.

/L
 
R

Richard Cornford

Opera 7.20 beta 2.

Strangely it is possible to dynamically load a script in the first beta
release of Opera 7 (7.0b1) by re-writing the innerHTML of a DIV to
include a new script tag. So it can be done in two betas but no official
releases.

Incidentally I don't think that I ever tested to see how (and if)
setting a - defer - property on a createElement('script') element to
true influenced the success rate when trying this.

Richard.
 
J

Jim Ley

I'm considering solutions like having zero-size frames in a frameset
that load other pages with JavaScript code in them, to avoid holding up
the loading of functional pages. Any other ideas?

framesets are generally a bad idea, a better idea would I think depend
on what you're doing and the nature of the interaction, and who the
audience is and ... I'm sure there is one though.

Jim.
 
C

Chris Smith

Lasse said:
Javascript objects are completely transparent. There is no more to
access than what an external observer can see.

If you add the so-called "private members" then it is not really
supported by Javascript objects, but it can be *emulated* by scoped
variables shared by the methods.

Yep. That was Richard's terminology, not mine, and I was adopting it.
So, it looks like it all comes down to two possible general approaches:

1. Add only members intended to be used by the outside world, and then
use closures and local variables and inner functions of the constructor
for other members. A few methods could be added to the prototype, but
only if they are the "convenience method" variety that can be entirely
expressed in terms of other object interface elements that are intended
to be exposed.

This seems to offer the best info-hiding behavior, but suffer from the
performance issues you mentioned.

2. Add all members to the object, and don't use Richard's trick with
closures at all. In this case, every method would be added to the
prototype. Then, back to my original question, would you use some kind
of identifier tagging such as a leading underscore to distinguish your
intended public interface from implementation artifacts.
You cannot hide anything from anybody using only Javascript objects.

That seems more than a bit disingenuous when you consider that Richard
has proposed to me a means of using closures to accomplish exactly that.
You can add the "defer" attribute to the script tags:
<script type="text/javascript" src="foo.js" defer="defer"></script>
All that means is that you promise that the source contains no
document.write's, and that parsing can continue without waiting for the
code to be loaded and run.

Great! This is exactly the sort of thing I was looking for.

--
www.designacourse.com
The Easiest Way to Train Anyone... Anywhere.

Chris Smith - Lead Software Developer/Technical Trainer
MindIQ Corporation
 
L

Lasse Reichstein Nielsen

Chris Smith said:
That seems more than a bit disingenuous when you consider that Richard
has proposed to me a means of using closures to accomplish exactly that.

That is why I said "using only Javascript *objects*". You can emulate
everything with closures[1], but if hiding methods or fields of objects
is so important, then maybe one should use a language that supports
it natively, without a hack (no matter how ingenious the hack is).

/L
[1] I mean *everything*. Closures, function calls and return statements
together are Turing complete, so they can emulate *any* computation. That's
mostly theoretical, since the emulation can be quite big in some cases,
but it suggests the power that closures have.
 
L

Lasse Reichstein Nielsen

Dr John Stockton said:
I *guess* the above really means that loading foo.js must *execute* no
document.write etc., without prohibiting loading functions that when
called will execute such.

Ofcourse :)
And that, if the browser does not honour defer, the rest still works.

It should. Adding "defer" is only a hint to the browser that it can do
an alternative interleaving of the parsing of the source and the
loading and executing of the javascript. It tells the browser that it
is safe to continue parsing before executing the code, but it is always
safe not to wait.

/L
 

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

No members online now.

Forum statistics

Threads
474,077
Messages
2,570,569
Members
47,205
Latest member
KelleM857

Latest Threads

Top