JavaScript 1.2's evolution as explained by its creatorBrendan Eich provides an enlightening description of 1.2's data types, as the language continues to search for its own identity and a universal standardBy Lisa Rein |
has become May 1997 |
|
Summary Is JavaScript a standard? Can something be a standard without a specification? A scripting standard that Netscape promises to follow is not far off, only the specification won't be called "JavaScript." How does the name "EcmaScript" grab you? We begin with a discussion about the unusual path that Netscape
has chosen to standardize the JavaScript language. Since there is no
single specification detailing JavaScript's syntax and usage, we
provide a collection of
every JavaScript reference document on Netscape's site.
We move on to discuss some of the new ways that JavaScript handles data
types, fleshing everything out with comments and examples from
JavaScript creator Brendan Eich. Eich explains what went wrong with
the Also presented is a coding scenario solution for providing backwards-compatibility with non-1.2-JavaScript-enabled browsers, featuring a handy checklist of the many varying factors to consider when coding JavaScript for backwards-compatibility in general. (3,900 words) |
|
|
Of these documents, many are obsolete (albeit nicely-indexed), all are poorly cross-referenced, and even occasionally contradictory. (The only consistency these documents share are their disclaimers.)
The last few months, the naming and usage of JavaScript's events and methods have been repeatedly altered, replaced, or "renamed for consistency" with the coming of each new Communicator Preview Release. Different preview versions and platform-specifics only add to the confusion. At present, although complete backwards-compatibility is touted, it is not explained, and in no way guaranteed. Perhaps JavaScript is the first example of a widely implemented scripting language that doesn't guarantee backwards-compatibility, nor does it offer any guarantees about its current functionality either.
For example, in Netscape's document titled "Codestock on JavaScript 1.2 Enhancements", in the "Q & A Session" on "Positioning and Layering HTML documents", one of the questions reads:
"The offset() method for layers is being renamed moveBy() for consistency with the naming of window methods. Will offset() still be supported for backwards compatibility?"
and Netscape's answer is:
"It will probably still work in the preview releases. It may be removed from the final release."
Another example can be found in the Release Notes for the "early" release of the Windows Communicator PR4. (Yes, the "early" release of a "preview.") In the "developer information" section, it reads:
"Every layer in JavaScript now has a DOCUMENT property
that scopes all the reflected HTML objects inside the layer.
Similarly, JavaScript code exists inside a <LAYER> tag will be
scoped to the containing layer rather than the window. THIS CHANGE
MAY BE INCOMPATIBLE WITH SCRIPTS DESIGNED TO WORK WITH COMMUNICATOR
PR2."
"That's just beta flux," said JavaScript creator Brendan Eich. "You have to allow for that if you start to develop for a beta. It really was a mistake that the folks working on window.moveBy and the folks working on layer.offset didn't get together ahead of time. We could leave behind a layer.offset synonym to make old beta code work, but why preserve a deprecated name and make our code more complicated if we can help it?"
ECMA has recently added five new members to its "General Assembly":
Also, a new Technical Committee was formed: ECMA TC39 - Scripting Languages. It's stated "scope" is "To standardize the syntax and semantics of a general purpose, cross-platform, vendor-neutral scripting language", and the Committee is chaired by Sun's Gary Robinson.
The main difference between Ordinary Members and Associate Members seems to be that Associate Members don't get a vote in the general assembly, only pay half of an Ordinary Member's dues, but there may be more to it, so see ECMA's bylaws for yourself.
Here's what ECMA's hierarchical structure looks like:
"You might want to mention that newlines and quotes can appear in a string if preceded by \, also the \076 and \x3e octal and hex escape sequences."
Boolean values are often generated by comparison operators in order to determine the correct path of action in a JavaScript's control structure. An if/else statement, for example, where the Boolean result is "true" will instruct JavaScript to perform one action while a "false" result will trigger a different action.
Boolean data types can have only two possible values: true and false ("on and off", "yes and no", or even "1 and 0").
"The sense of true and false can be thought of as "yes" and "no" respectively, but some new JS authors might take the above to mean that "0" is equivalent to false. It's true that 0 converts to false, as does the empty string "" or '', but "no" converts to true because it's a non-empty string.There are also objects, and arrays: data types that are more complex and are compared by reference. Two object or array variables are only equal if they refer to the same object, array or function. So two separate arrays will never be equal, by definition, even if they contain the same elements."I've found you can't be too careful in explaining the conversion rules, from experience with developers and the ECMA standardization process."
"Null" is a special value for an object variable that indicates "no value", meaning it doesn't contain a valid object, array, number, string, Boolean value, or function.
"Actually, you'll get an error if you use (a variable) without first setting it (thus creating a global or top-level variable), or declaring it with var."A variable will return an "Undefined" value if you've used an object property that doesn't exist, or a variable that was declared, but never had a value assigned to it.
So if we write:
my.prop == null
it will return true if either my.prop doesn't exist or if it exists but contains the value null.
However, if you use typeof on null, it returns "undefined".
"That's because JS has until 1.2 considered "undefined" to be equal to
"null". In 1.2 we're tightening things up so they aren't equal. The
simplest way to test whether a property or variable that should
refer to an object is not undefined and not null, in all versions of
JS, is:"
if (obj.prop) {
// obj.prop refers to a valid object
}
If you attempt to append a number to a string, JavaScript will automatically convert the number to its corresponding string so that it can be appended.
"It's probably helpful to point out that this situation arises only with +, which means string concatenation if either operand is a string or object that converts to a string.You can convert a string to a number many ways (roughly in order of efficiency and clarity):"
Number("1") + 2 == 3 ("1" - 0) + 2 == 3 parseInt("1") + 2 == 3 eval("1") + 2 == 3
With comparison operators, the definition of "equal" depends on the data type (numbers, strings, or Boolean). Two numbers must have exactly the same numeric value. Two strings are equal only if they each contain exactly the same characters.
Usually, if two values have different data types, by definition, they are not equal.
"This is true always (not usually) only in JS1.2. In earlier versions,
and if you don't specify a version (SCRIPT LANGUAGE=JavaScript), then
conversions are applied as follows:"
if (both operands are objects)
compare object references;
else if (either operand is null)
convert the other to object and compare references;
else if (both operands convert to string)
compare string conversions by value;
else
convert to number and compare;
If the expression "1" == 1 evaluates to true, and true == 1 and false == 0, are both expressions not true because they happened to have string values that converted easily to numeric values?
"Not so -- neither true, nor false, nor 0 nor 1 are strings, nor do they need to be converted to string in order for JS (old or unspecified versions) to consider them to be equivalent: if you plug and chug in the pseudo-code above, you'll find they all convert directly to number (false to 0 and true to 1) and compare accordingly, as numbers."
Navigator 2.0 will also incorrectly return true for Null == 0, but that's fixed in Navigator 3.0.
If you do accidentally compare a string to a number and the string cannot be converted to a number, then Navigator 2.0 and 3.0 will produce an error message, while IE 3 will simply return "false". So you need to be careful of the data types when working with your variables, or an incorrect Boolean value will trigger the wrong action.
"Another bug, fixed in 4.0: undefined and strings containing non-numeric literals convert to NaN (Not a Number), a special IEEE754 floating point pattern (set of patterns, really) that does not equal any other number or itself, and is neither less than nor greater than any other number."The equality operators (== and !=) work differently if the
SCRIPT tag uses LANGUAGE=JavaScript1.2. If
either LANGUAGE=JavaScript1.1 or
LANGUAGE=JavaScript are used, the equality operators will
maintain their previous behavior, which is:
If LANGUAGE=JavaScript1.2 is used, the equality operators (==, !=) use a different criteria when comparing mismatched data types in order to return a Boolean value.
For this reason, you must convert operands manually when writing for all JavaScript versions of Navigator:
To convert x to a string, use " " + x. For example,
To convert x to a number, use (x - 0). For example,
When writing for JavaScript 1.2 only:
To convert x to a string, in addition to " " + x, you can use String(x) when you declare the variable. For example,
"There's no need to introduce a variable here, and I'm afraid new users will fear they have to do so, and proliferate useless variables superstitiously. Better to minimize:"
String(3) == "3"
To convert x to a number, in addition to (x - 0), you can use Number(x). For example,
Examples
| Example | Boolean Value | Because/Precedence | So |
|---|---|---|---|
| <SCRIPT> document.write("3" == 3); </SCRIPT> | TRUE** | old model wants to convert one of them and numbers take precedence over strings, unless + | ("3" == 3) or ((3-0) == 3) or 3=3 |
| <SCRIPT> document.write(("3"-0) == 3); </SCRIPT> | TRUE | parentheses forces "3" to convert to number to perform subtraction operator first (which requires number values) | ((3-0)-0)==3 or ((3-0)==3) or (3==3) |
| <SCRIPT LANGUAGE="JavaScript"> document.write("3" == 3); </SCRIPT> | TRUE** | wants to convert one of them and numbers take precedence over strings, unless + | ("3" == 3) or ((3-0) == 3) or 3=3 |
| <SCRIPT LANGUAGE="JavaScript1.1"> document.write("3" == 3); </SCRIPT> | TRUE** | wants to convert one of them and numbers take precedence over strings, unless + | ("3" == 3) or (3-0) == 3 or 3=3 |
| <SCRIPT LANGUAGE="JavaScript1.2"> document.write("3" == 3); </SCRIPT> | FALSE | Inherently false because different data types. | |
| <SCRIPT LANGUAGE="JavaScript1.2"> document.write(("3"-0) == 3); </SCRIPT> | TRUE | parentheses cause "3" to be converted to number due to requirement of subtraction operator | ((3-0)-0)==3 or ((3-0)==3) or (3==3) |
| <SCRIPT LANGUAGE="JavaScript1.2"> document.write(String(3) == "3"); </SCRIPT> | TRUE | 1) Both are strings and 2) they contain exactly the same characters | ("3"=="3") or "3"=="3" |
**NOTE regarding row of table:
The following rule will be revised in the forthcoming ECMA standard for JavaScript (EcmaScript):
<SCRIPT>
document.write("3" == 3); numbers take precedence
</SCRIPT> over strings, unless +
"The old model wants to convert one of them, and numbers take precedence over strings, unless "+" is used, but that will no longer be the case. The new standard will give strings precedence."What's the difference, you ask? Converting to number rather than string ("numbers take precedence") means both "3" and "3.0" (and "3.00", etc.) equal 3, but "3.0" does not equal "3" -- the relations "3" == 3 and 3 == "3.0" do not imply "3" == "3.0".
"This means == is not transitive. Transitivity is considered desirable for equality operators. Equality operators, according to purists, should be equivalence relations (transitive, reflexive and symmetric).
"So for the ECMA standard, strings will take precedence and there is exactly one string conversion of a given number: 3 converts to "3", not to "3.0" or "3.00". So the result is that "3" == 3 but "3.0" != 3, in the ECMA standard."
We will be standardizing our extensions via ECMA. The ECMA TC39.1 group is finishing up the standard for a June ECMA general assembly vote, I believe.
What's your stand on the <LAYER> tag and the way Netscape has moved
forward with its implementation apparently in spite of its apparent parallel implementation of the CSS1 standard?
First, Netscape co-authored the CSS draft standard for positioning containers, or whatever they're called.Second, our customers want tags, which are more concise, expressive, and compatible with HTML "programming" than CSS.
Why do we need <LAYER> when there can be a cross-browser solution using HTML and CSS?
People who want to code for browsers not supporting<LAYER>may find the things they can do limited by CSS. If they want to do dynamic layers, they'll have to use another vendor's proprietary object model scripting anywWe expect to participate through the W3C in HTML object model standardization, but that has not really started yet.
Why you would need to "signed scripts" for since JavaScript can't make system calls anyway?
But it can, via Java. A signed-script whose principals you trust to write a file can do so using LiveConnect.And even without Java, there are many operations that we've forbidden unsigned JS from doing (sending mail, looking at another window's history if the window loads a document from a different server, etc.).
Am I to understand that if you wanted to code backward compatibility you'd have to not only use the language attribute for 1.0, 1.1 and 1.2, but hide all of the contents in comment tags?
Sure. The comment-hack is needed for browsers that don't grok SCRIPT at all.I suspect no one would bother with
LANGUAGE=JavaScript1.2in addition to a least-common-denominator script, just for the sake of using both sets of operators or because the new equality operators are simpler to define.Why then, use
LANGUAGE=JavaScript1.n"at all?Well, for precisely the same reason that we added
LANGUAGE=JavaScript1.1in 3.0 to help users hide scripts that updated image source URLs from Navigator 2.0 users, the real payoff for the extra work of aLANGUAGE=JavaScript1.2script will only seem worthwhile once you start using the new killer features (regular expressions, layers, etc.) to add real pizzazz to your page.
Is it possible to define an object and call to it one way with JavaScript 1.2, call to it another way with JavaScript 1.1, and theoretically call it yet a third way with JavaScript 1.0, without having the various events getting in each other's way. Does Communicator just "know" which, if any, functions/events won't work together so it picks one (presumably 1.2...)
No, it reads them all in order. So if you want your 1.2 functions to trump any older versions, put the 1.2 script tag last.
Why go to ECMA?
Many reasons, among them ECMA's reputation for fairness and speed of standards process.
When do you estimate JavaScript becoming any kind of standard?
There is an ECMA General Assembly meeting in June at which I believe a vote will be held on the "EcmaScript" specification. We believe it should be approved and then become an ECMA standard, with a freely available and rigorous language specification.
Any why do you keep changing the names of events and things, such as the LAYER method offset() that was renamed to moveBy(), when that could jeopardize backwards-compatibility?
What about window.innerHeight and window.innerWidth then? From a developer's standpoint, doesn't that just complicate things?
Developers were finding their old code that set (but never declared with var) "width" and "height" variables would break -- would set the window's inner width and height instead of setting their variables -- in 4.0PR2 or whichever it was that we shipped window.width and window.height as properties.Again, we have to be able to fix beta bugs including naming bugs, or what's the point of beta? A beta feature is not as strong a promise of compatibility as a final .0 release feature, although we don't take name changes lightly even in betas. For window.width and window.height , we had no choice -- those names are too common in code developed for 3.0 and 2.0.
We could have tried to use
LANGUAGE=JavaScript1.2as a necessary condition for defining the window.width and window.height properties, but that leads to complicated tests in our code, and likely confusion for users. We'd rather extend existing the window object with names that are less likely to collide with names that developers have already chosen for their global variables.And where possible, we let the developer's name "win" and hide the new name we're trying to predefine. We do this by "lazy" or "late" binding -- only when the developer's script refers to "screen" but does not assign to it for instance, do we make the new screen object available. We didn't use this technique for innerWidth and innerHeight, or a few other pairs of new properties, however, to avoid undue complications in our source code. We haven't found scripts that use those names already, but there are many that use "width" and "screen" for their own variable and function names.
Note that we did keep "width=" and "height=" window.open options, of course -- no way to remove those without breaking the world, even though we do provide the "innerWidth=" and "innerHeight=" option synonyms. As a matter of style, new
LANGUAGE=JavaScript1.2scripts should probably use "innerWidth=" and "innerHeight=" to be clear -- the old names are of course required forLANGUAGE=JavaScript, and what they mean is well-defined, but their names do not connote "inner," at least to me.We want to assure people that we won't be adding names carelessly in the future, and that we hope to have other ways of separating our predefined names from names defined by developers.
At first glance, JavaScript's backwards-compatibility strategy seems very straight forward, almost foolproof, but the details get very confusing very fast.
There are several approaches to providing backwards-compatibility to the visitors of your site. One way is to code two completely different pages and use a script to figure out who's got what browser and then serve them that page accordingly.
Another option is to code for multiple versions for JavaScript and let the browsers decide.
Here's a checklist to use when using several SCRIPT
LANGUAGE= tags in a single document for preserving
backwards-compatibility.
SCRIPT LANGUAGE attribute at all times.SCRIPT tag to indicate which
version of JavaScript you're using.
Since statements within a SCRIPT tag are ignored if
the browser does not have the level of JavaScript support specified
in the LANGUAGE attribute, the browser will keep going until it sees
something it can interpret.
By using the LANGUAGE attribute, you can write general JavaScript that Navigator version 2.0 and higher recognize, while still including additional or refined behavior for newer versions of Navigator.
If you use SCRIPT LANGUAGE="JavaScript1.2", you need
to be aware that JavaScript 1.2 will only work in Navigator, 1.1
will work in Netscape 3.0 and IE 4.0, and 1.0 will work in Navigator
2.0 and IE 3.0.
SCRIPT codeSCRIPT with comment tags (<!-- and //-->) or the
non-JavaScript-aware browsers will write your events and function
calls as text on the screen.
Older browsers that don't recognize the SCRIPT tag will
pass right over it, and render its contents as text Conversely,
when JavaScript-aware browsers scan the SCRIPT tag's
contents, they recognize the comment tag as the first line of a
JavaScript program, so they execute the instructions. In fact,
JavaScript has its own comment tag:
document.write(3-"0");
// this is how you'd leave a comment
// you can leave as many as you like without them being read
So if your coding for backwards-compatibility, you better comment
out the contents of all your SCRIPT
LANGUAGE="JavaScriptX.X" tags, even though Communicator
doesn't need this precaution, all the people you want the page to
degrade to will.
Like this:
<SCRIPT LANGUAGE="JavaScript">
<!--
hacks_img=null;
function whack_image(img) { }
//here is where yo would put your comments
//-->
</SCRIPT>
<SCRIPT LANGUAGE="JavaScript1.1">
<!--
function whack_image_name(img, i) {
return img.src.substring(0, img.src.length - 5) + i +
img.src.substring(img.src.length - 4);
}
function whack_image(img) { img.src = whack_image_name(img, "2"); }
//pretty darned exciting, isn't it!
//-->
</SCRIPT>
Terminating your statements from the get-go will also with the process of elimination when you're troubleshooting your code later.
HEADHEAD of your
document, where they will wait in your user's cache patiently until
invoked by a user-event or conditional statement.
You can also use hidden frames or pixel-sized images to do tricks for you, but how those kinds of tricks behave "out there" on the browsers of the world will be entirely out of your control.
If you have a working example of JavaScript Functionality that gracefully degrades from JavaScript 1.2 to 1.1 to 1.0 to a text file, let us know.
JavaScript 1.0 references
|
About the author
Reach Lisa Rein at
lisa.rein@netscapeworld.com.
HYIP Monitor