Compare Revisions
Last modification
- Rev 44 2015-12-12 19:43:26
- Author: PointedEars
- Log message:
cljs: Deleted old duplicate FAQ notes
/trunk/cljs/faq_notes/clj_posts.html |
File deleted |
|
Property changes: |
Deleted: svn:mime-type |
## -1 +0,0 ## |
-text/plain |
\ No newline at end of property |
Index: cljs/faq_notes/not_browser_detect.html |
=================================================================== |
--- cljs/faq_notes/not_browser_detect.html (revision 43) |
+++ cljs/faq_notes/not_browser_detect.html (nonexistent) |
@@ -1,1413 +0,0 @@ |
-<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" |
- "http://www.w3.org/TR/html4/strict.dtd"> |
-<html lang="en"> |
-<head> |
-<title>Browser Detection (and What to Do Instead)</title> |
-<meta HTTP-EQUIV="Content-Type" CONTENT="text/html; charset=iso-8859-1"> |
-<link href="../../faq.css" rel="stylesheet" type="text/css"> |
-<link href="../notes.css" rel="stylesheet" type="text/css"> |
-<style type="text/css"> |
-.tip { |
- border: 1px solid #ccc; |
- padding: 1ex; |
- background: #fcfcfc; |
- font-size: 90%; |
-} |
-</style> |
-</head> |
-<body> |
-<h1 id="bdTop">Browser Detection (and What to Do Instead)</h1> |
-<div id="faqNav"> |
- <a href="../../">FAQ</a> > <a href="../">FAQ Notes</a> |
-</div> |
-<p> |
-By Richard Cornford, edited by Garrett Smith |
-</p> |
-<ul> |
- <li><a href="#bdIntro">Introduction</a></li> |
- <li><a href="#bdValid">Avoiding Structural Differences in the Browser DOMs</a></li> |
- <li><a href="#bdDif">Browsers Differences</a></li> |
- <li><a href="#bdFailS">Failed Strategies: Browser Detecting</a> |
- <ul> |
- <li><a href="#bdUAS">Assumptions Based on navigator.userAgent</a></li> |
- <li><a href="#bdOI">Assumptions Based on DOM Objects: Object inference</a></li> |
- </ul> |
- </li> |
- <li><a href="#bdFD">A Strategy That Works: Object/Feature Detecting.</a> |
- <ul> |
- <li><a href="#bdGEID">Example 1: IDed Element Retrieval</a></li> |
- <li><a href="#bdScroll">Example 2: Scroll Values</a></li> |
- <li><a href="#bdReplace">Example 3: String.prototype.replace</a></li> |
- </ul> |
- </li> |
- <li><a href="#bdDesPb">The Javascript Design Problem</a></li> |
- |
-</ul> |
- |
-<h2 id="bdIntro">Introduction</h2> |
- |
-<p id="bdIntro_1"> |
-Under normal circumstances computer programs are written for a known |
-environment. The programmer knows what to expect; which APIs will be |
-available, how they will behave and so on. Java is an ideal example |
-of this, providing a theoretically consistent set of APIs and language |
-features across all Java Virtual Machine (JVM) implementations. But |
-this is also true in most other circumstances. The programmer of C++ |
-for the Windows operating system will know what MFC classes are |
-available and how to program them. Their expectations will be |
-rewarded, if they posses the required knowledge. |
-</p> |
- |
-<p id="bdIntro_2"> |
-Client side javascript for the Internet, on the other hand, has the |
-possibly unique problem of having to be authored with no specific |
-knowledge of the environment in which it will be executed. The |
-client side environment will usually be a web browser and web |
-browsers do tend to have many common features (and increasingly |
-standardised features) but the author cannot know which version of |
-which browser will be making any HTTP request (or |
-whether it is a browser at all). It is not even possible to tell in |
-advance whether the User Agent will be capable of executing |
-javascript; all of those that can include a user configurable option |
-to disable it anyway. |
-</p> |
- |
-<p id="bdIntro_3"> |
-Javascript authors for the Internet must realise that they are dealing |
-with the unknown and that any, and all, scripts will fail to execute |
-somewhere. The challenge is to get the most from your javascript when |
-it is available but to cope with their failure in a meaningful way. |
-</p> |
- |
-<p id="bdIntro_4"> |
-I once read a description of a variant on the game of chess, played |
-at military academies. Two players sit at separate boards set up with |
-only their own pieces, out of sight of each other, and a referee |
-supervises the game. Each player makes their move in turn and the |
-referee is responsible for informing them how the other's move impacts |
-on their own pieces and how the other's disposition of pieces impact |
-on their intended move. The player is informed only when one of their |
-own pieces is taken, when one of their moves is affected by |
-interacting with one of their opponents pieces (i.e. a player may want |
-to move a bishop across the board but the referee may inform them that |
-their move was stopped four squares early when the bishop took a pawn |
-from the other side) and when one of their opponents pieces is |
-sitting on a square adjacent to one of their own. |
-</p> |
- |
-<p id="bdIntro_5"> |
-The game is used to teach battlefield strategy. To win a player must |
-probe and test to deduce his opponent's disposition, while planing and |
-executing a response that will achieve the desired checkmate. It is |
-this sort of strategy that needs to be added to the normal programming |
-problems in order that javascript may cope with its unknown execution |
-environment, with the significant difference that the strategy, its |
-tests and all of the paths of execution must be fully planed out before |
-the code can even starts executing. |
-</p> |
- |
-<h2 id="bdValid">Avoiding Structural Differences in the Browser DOMs</h2> |
- |
-<p id="bdValid_1"> |
-While the point of this article is to introduce techniques for handling |
-the differences between web browsers and their DOM implementations it |
-is also possible to avoid some types of differences especially related |
-to the structure of the DOM that is being scripted. |
-</p> |
- |
-<p id="bdValid_2"> |
-If I was asked to recommend one action most likely to promote the |
-authoring of cross-browser scripts it would be: <strong><em>Start |
-from a basis of valid <span class="initialism" title="HyperText Mark-up Language"><abbr title="HyperText Mark-up Language" |
->HTML</abbr></span></em></strong>.</p> |
- |
-<p id="bdValid_3"> |
-Browsers presented with invalid <span class="initialism" title="HyperText Mark-up Language"><abbr title="HyperText Mark-up Language">HTML</abbr></span> will usually attempt to error |
-correct it in order to do the best possible job of displaying it. |
-Some browsers, particularly IE, are tolerant of all sorts of strange |
-formulations of mark-up. Valid <span class="initialism" title="HyperText Mark-up Language"><abbr title="HyperText Mark-up Language">HTML</abbr></span> has a tree-like structure, elements |
-may completely contain others but they cannot overlap, and there are |
-rules about which elements may appear in which contexts. The DOM that |
-is to be scripted also has a tree-like structure and there is a very |
-simple relationship between the tree-like structure of valid <span class="initialism" title="HyperText Mark-up Language"><abbr title="HyperText Mark-up Language">HTML</abbr></span> and |
-the DOM constructed from it. So any browser presented with valid <span class="initialism" title="HyperText Mark-up Language"><abbr title="HyperText Mark-up Language">HTML</abbr></span> |
-will be able to directly translate that <span class="initialism" title="HyperText Mark-up Language"><abbr title="HyperText Mark-up Language">HTML</abbr></span> into a corresponding DOM |
-using well specified rules, resulting in a DOM that is of predictable |
-and consistent structure on all of the browsers that can build a DOM. |
-</p> |
- |
-<p id="bdValid_4"> |
-Invalid <span class="initialism" title="HyperText Mark-up Language"><abbr title="HyperText Mark-up Language">HTML</abbr></span> will not translate as naturally into a DOM, or even a |
-tree-like structure. If the browser is going to build a DOM with the |
-source provided it is going to have to apply error correcting rules |
-and attempt to build the best DOM it can. But the error correcting |
-rules are not standardised, not even published. So different browsers |
-have no choice but apply different rules and that directly results in |
-the building of DOMs with different (and in extremes, radically |
-different) structures. |
-</p> |
- |
-<p id="bdValid_5"> |
-As a result, using invalid <span class="initialism" title="HyperText Mark-up Language"><abbr title="HyperText Mark-up Language">HTML</abbr></span> directly produces differences in the |
-DOMs produced by different browsers. No matter how good the application |
-of techniques for dealing with the differences between browsers, it |
-does not make sense to do anything that will provoke more differences |
-than are unavoidable. |
-</p> |
- |
-<p id="bdValid_6"> |
-The authoring of invalid <span class="initialism" title="HyperText Mark-up Language"><abbr title="HyperText Mark-up Language">HTML</abbr></span>, justified because "It works in |
-browser XYZ", gives the authors of accompanying scripts the |
-impression that cross-browser scripting is harder than it is. If they |
-had started with valid <span class="initialism" title="HyperText Mark-up Language"><abbr title="HyperText Mark-up Language">HTML</abbr></span> they would never have encountered any of |
-the structural inconsistencies that invalid <span class="initialism" title="HyperText Mark-up Language"><abbr title="HyperText Mark-up Language">HTML</abbr></span> can provoke. |
-</p> |
- |
-<h2 id="bdDif">Browsers Differences</h2> |
- |
-<p id="bdDif_1"> |
-As browsers have evolved they have offered more features to javascript. |
-Different manufactures have adopted the features of other browsers, |
-while adding new features, that may in turn have been adopted by (or |
-will be adopted by) their competitors. Various sets of common features |
-have emerged and some have been formalised by the W3C into a sequence |
-of standard DOM specifications. Along the way an increasing number of |
-javascript capable browsers have emerged. In addition to desktop PC |
-browsers, javascript capable browsers exist for a whole range of |
-devices; PDAs, mobile telephones (Microwave ovens, refrigerators). |
-</p> |
- |
-<p id="bdDif_2"> |
-Unfortunately it is the case that the browsers on the smaller devices |
-cannot offer the range of features available to a desktop PC and even |
-as the technology improves and features are added to the smaller |
-browsers the problem will not improve as browsers will become available |
-on a wider range of devices while the desktop PC browsers will continue |
-to march ahead of them. |
-</p> |
- |
-<p id="bdDif_3"> |
-Over the years various strategies have been attempted to tackle this |
-problem and some have failed miserably. |
-</p> |
- |
-<h2 id="bdFailS">Failed Strategies: Browser Detecting</h2> |
- |
-<h3 id="bdUAS">Assumptions Based on navigator.userAgent</h3> |
- |
-<p id="bdUAS_1"> |
-One of the most popular strategies for handling the differences between |
-web browsers was browser detecting based on the User Agent string. |
-Browsers possessing a <code>navigator</code> object also provide a |
-property of that object: <code>navigator.userAgent</code> containing a |
-string that (in theory) identifies the browser. Its application went |
-something like:- |
-</p> |
- |
-<pre id="bdUAS_ex1"> |
-<span class="commentJS">/* Warning: never use this script, or any script based on, or resembling, it. |
-*/</span> |
-var userAgent = self.navigator.userAgent; |
-var appName = self.navigator.appName; |
-var isOpera = false; |
-var isOpera5 = false; |
-var isOpera6p = false; |
-var isIE = false; |
-var isIE4 = false; |
-var isIE5p = false; |
-var isMozilla1p = false; |
-var isNet4 = false; |
-var isNet5p = false; |
-var operaVersion = 0; |
-var ieVersion = 0; |
-var appVersion = self.navigator.appVersion; |
-var brSet = false; |
- |
-function brSetup(){ |
- for(var c = 3;c < appVersion.length;c++){ |
- var chr = appVersion.charAt(c); |
- if(isNaN(chr)){ |
- appVersion = appVersion.substring(0, c); |
- break; |
- } |
- } |
- if((userAgent.indexOf('webtv') < 0) && |
- (userAgent.indexOf('hotjava') < 0)){ |
- if(userAgent.indexOf('Opera') >= 0){ |
- var ind = (userAgent.indexOf('Opera')+6); |
- if(((ind+1) < userAgent.length)&&(ind >= 6)){ |
- isOpera = true; |
- var bsVersion = parseInt(userAgent.substring(ind, ind+1)); |
- if(!isNaN(bsVersion)){ |
- operaVersion = bsVersion; |
- if(operaVersion >= 6){ |
- isOpera6p = true; |
- }else if(operaVersion >= 5){ |
- isOpera5 = true; |
- } |
- } |
- } |
- }else if(appName.indexOf('Microsoft Internet Explorer') >= 0){ |
- var ind = (userAgent.indexOf('MSIE')+5); |
- if(((ind+1) < userAgent.length)&&(ind >= 5)){ |
- isIE = true; |
- var bsVersion = parseInt(userAgent.substring(ind, ind+1)); |
- if(!isNaN(bsVersion)){ |
- ieVersion = bsVersion; |
- if(ieVersion >= 5){ |
- isIE5p = true; |
- }else if(ieVersion >= 4){ |
- isIE4 = true; |
- } |
- } |
- } |
- }else if(appName.indexOf('Netscape') >= 0){ |
- if((self.navigator.vendor)&& |
- (self.navigator.vendor.indexOf('Netscape') >= 0)&& |
- (userAgent.indexOf('Gecko') >= 0)){ |
- isNet5p = true; |
- }else if((userAgent.indexOf('Netscape') < 0)&& |
- (userAgent.indexOf('Gecko') >= 0)&& |
- (appVersion >= 5)){ |
- isMozilla1p = true; |
- }else if((appVersion < 5)&& |
- (userAgent.indexOf('compatible') < 0)){ |
- isNet4 = true; |
- } |
- } |
- } |
- brSet = true; |
-} |
-</pre> |
- |
-<p id="bdUAS_2"> |
-This version also uses some other properties of the |
-<code>navigator</code> object; <code>appName</code> and |
-<code>appVersion</code>. |
-</p> |
- |
-<p id="bdUAS_3"> |
-Superficially this type of script seems to be saying quite a lot about |
-what browser is executing the script. Knowing that the |
-<code>isIE5p</code> variable is boolean <code>true</code> seems to be |
-a reasonable indicator that the browser in question is Internet |
-Explorer Version 5 or above and from that all of the available features |
-on the IE 5+ DOM could be assumed to exist. |
-</p> |
- |
-<p id="bdUAS_4"> |
-Unfortunately, if this type of script ever was an effective determiner |
-of the browser type, it is not now. The first problem is that you cannot |
-write this type of script to take into account all web browsers. The |
-script above is only interested in Internet Explorer, Netscape and |
-(some) Mozilla derived browsers and Opera. Any other browser will not |
-be identified, and that will include a number of W3C DOM conforming |
-fully dynamic visual browsers quite capable of delivering on even quite |
-demanding code. |
-</p> |
- |
-<p id="bdUAS_5"> |
-The second problem is that scripts like this one, and server-side |
-counter-parts (reading the HTTP User Agent header) were used to |
-<em>exclude</em> browsers that did not fall into a set of browsers |
-known to the author, regardless of whether those browsers were |
-capable of displaying the offending site or not. |
-</p> |
- |
-<p id="bdUAS_6"> |
-As more browsers were written, their authors discovered that if they |
-honestly reported their type and version in their User Agent string |
-they would likely be excluded from sites that they would |
-otherwise be quite capable of displaying. To get around this problem |
-browsers began spoofing the more popular versions, sending HTTP User |
-Agent headers, and reporting <code>navigator.userAgent</code> strings, |
-that were indistinguishable from, say, IE. |
-</p> |
- |
-<p id="bdUAS_7"> |
-As a result, when the above script reports <code>isIE5p</code> as true, it is |
-possible that the browser that is executing the script is one of |
-numerous current browsers. Many of those browsers support sufficient |
-features found on IE5+ to allow most scripts to execute but the |
-trueness of <code>isIE5p</code> is not a valid indicator that the |
-browser will support <em>all</em> of the IE 5+ DOM. |
-</p> |
- |
-<p id="bdUAS_8"> |
-Now you might decide that a browser that lies about its identity |
-deserves what it gets (though they started lying in order to make |
-themselves usable in the face of near-sighted <span class="initialism" title="HyperText Mark-up Language"><abbr title="HyperText Mark-up Language">HTML</abbr></span> and script authors) |
-but is worth bearing in mind that the IE 5 |
-<code>navigator.userAgent</code> string is: |
-<code>"Mozilla/4.0 (compatible; MSIE 5.01; Windows NT)"</code>. |
-IE 5 is in fact spoofing Netscape 4, and Microsoft started to do that |
-for precisely the same reasons that motivate many current browsers to |
-send User Agent headers, and report <code>navigator.userAgent</code> |
-strings that are indistinguishable form those of Microsoft browsers. |
-</p> |
- |
-<p id="bdUAS_9"> |
-No browser manufacture wants (or ever has wanted) their browser to be |
-needlessly excluded from displaying a web site that it is perfectly |
-capable of handling just because the author of that site does not |
-know it by name. And to prevent that they have followed Microsoft and |
-taken action that has rendered the <code>userAgent</code> string (and |
-the HTTP User Agent header) meaningless. |
-</p> |
- |
-<p id="bdUAS_10"> |
-We are now at a point where the contents of the User Agent strings |
-bear no relationship at all to the capabilities and features of the |
-browser that is reporting it. The situation has gone so far that a |
-number of javascript experts have stated that a standard quality test |
-for an unknown script would include searching the source code of the |
-script for the string <code>"userAgent"</code> and dismissing |
-the script out of hand if that string is found. |
-</p> |
- |
-<h3 id="bdOI">Assumptions Based on DOM Objects: Object inference</h3> |
- |
-<p id="bdOI_1"> |
-A second browser detecting strategy uses the objects present in various |
-browser DOMs and make the assumption that the presence (or absence) of |
-one or more objects indicates that a browser is a particular type or |
-version. I quickly found this example of typical code of this type:- |
-</p> |
- |
-<pre id="bdOI_ex1"> |
-<span class="commentJS">/* Warning: never use this script, or any script based on, or resembling, it. |
-*/</span> |
-var isDOM=(document.getElementById)?true:false; |
-var isIE4=(document.all&&!isDOM)?true:false; |
-var isIE5p=(document.all&&isDOM)?true:false; |
-var isIE=(document.all)?true:false; |
-var isOP=(window.opera)?true:false; |
-var isNS4=(document.layers)?true:false; |
-</pre> |
- |
-<p id="bdOI_2"> |
-Javascript performs automatic type conversion so when a boolean result |
-is expected from an expression that evaluates to a non-boolean value |
-that non-boolean value is (internally) converted to a boolean value |
-(using the rules defined in the ECMAScript specification) and that |
-boolean is used as the result. |
-</p> |
- |
-<p id="bdOI_3"> |
-Take the first line:- |
-</p> |
- |
-<pre id="bdOI_ex2"> |
-var isDOM=(document.getElementById)?true:false; |
-</pre> |
- |
-<p id="bdOI_4"> |
-The conditional expression requires that the expression preceding the <code>?</code> |
-have a boolean result. The <code>document.getElementById</code> |
-property accessor can resolve as one of two values depending on whether |
-the <code>getElementById</code> function is supported in the browser in |
-question. If it is supported then the accessor resolves as a function |
-object, and is type converted to boolean <code>true</code>. If |
-<code>getElementById</code> is not supported the accessor resolves as |
-undefined, and undefined type converts to boolean |
-<code>false</code>. Thus the expression preceding the question mark |
-resolves as <code>true</code> or <code>false</code> and based on that |
-result <code>true</code> or <code>false</code> are assigned to the |
-variable <code>isDOM</code>. |
-</p> |
- |
-<div class="tip"> |
- <h4>Boolean Conversion Tip: !!</h4> |
- <p id="bdOI_5"> |
- Incidentally, this code is not the optimum method of assigning a boolean |
- value based on the type converted to boolean result of a property accessor. |
- It is better to use the javascript NOT operator ( <code>!</code> ) twice |
- or to pass the object reference as the argument to the <code>Boolean</code> |
- constructor called as a function. The not operator will type convert its |
- operand to boolean and then invert it so <code>false</code> becomes |
- <code>true</code> and <code>true</code> becomes <code>false</code>. |
- Passing that result as the operand for a second not operator inverts |
- the boolean again so a reference to a function object results in boolean |
- <code>true</code> and an undefined reference results in boolean |
- <code>false</code>. The <code>Boolean</code> constructor called as a |
- function converts its argument to boolean and returns that value. The |
- statement would become:- |
- </p> |
- |
- <pre id="bdOI_ex3"> |
- var isDOM = !!document.getElementById; |
- |
- <span class="commentJS">/* - or - */</span> |
- |
- var isDOM = Boolean(document.getElementById); |
- </pre> |
- |
- <p id="bdOI_6"> |
- Which is shorter and faster than the original version and certainly |
- more direct. |
- </p> |
-</div> |
- |
-<h4>Inductive Generalization Fallacy</h4> |
-<p id="bdOI_7"> |
-The problem with this type of browser detecting script is that it is |
-used to make assumptions about the browser's capabilities that are |
-rarely valid. For example, this <code>isDOM</code> result, based on |
-the browser's support for <code>document.getElementById</code>, is |
-often used as the basis for the assumption that the browser has a |
-fully dynamic DOM with methods such as |
-<code>document.createElement</code>, <code>replaceChild</code> and |
-<code>appendChild</code>. Browsers do not live up to that expectation, |
-some are not that dynamic and while they may implement some of the Core |
-DOM level 1 methods such as <code>getElementById</code> They do not |
-necessarily implement large parts of the various DOM standards, |
-including all of the dynamic <code>Node</code> manipulation methods. |
-</p> |
- |
-<p id="bdOI_8"> |
-The result of the <code>isIE5p</code> test is intended to indicate that |
-the browser is Internet Explorer 5.0 or above. However, Opera 7, |
-IceBrowser 5.4, Web Browser 2.0 (palm OS), Konquerer, Safari, NetFront, |
-iCab and others will all produce a <code>true</code> value in |
-<code>isIE5p</code> because they implement <code>getElementById</code> |
-and the <code>document.all</code> collection. As a result, code that |
-assumes that it will have <em>all</em> of the capabilities of IE 5.0+ |
-available to it when <code>isIE5p</code> is <code>true</code> will as |
-often as not be mistaken. |
-</p> |
- |
-<p id="bdOI_9"> |
-This problem applies to all of the tests above with the possible |
-exception of the <code>window.opera</code> test. I am unaware of a |
-second browser type that has implemented an <code>opera</code> object |
-on the window object. But then Opera 7 is a radically different, and |
-much more dynamic browser that its preceding versions, though they all |
-possess a <code>window.opera</code> object. |
-</p> |
- |
-<p id="bdOI_10"> |
-To get around the problem that multiple browsers implement the same |
-features (even if they start off unique to one browser) script authors |
-have attempted to find more discriminating features to test. For |
-example, the following script extract is intended to work only on IE |
-5.0+ browsers:- |
-</p> |
- |
-<pre id="bdOI_ex4"> |
-var isIE5p = !!window.ActiveXObject; |
-... |
-function copyToClip(myString){ |
- if(!isIE5p) return; |
- window.clipboardData.setData("text",myString); |
-} |
-</pre> |
- |
-<p id="bdOI_11"> |
-The <code>ActiveXObject</code> constructor is intended to be |
-discriminating of an IE browser. However, this type if script still |
-does not work. It has placed the competing browser manufacturers in |
-exactly the same position as they were in when scripts tested the |
-<code>navigator.userAgent</code> string and excluded them from |
-accessing a site because they honestly reported that they where not |
-IE. As a result I already know of one browser that has implemented |
-a <code>window.ActiveXObject</code> function, it probably is a dummy |
-and exists in the browsers DOM specifically to defeat the exclusion |
-of that browser based on tests like the one above. |
-</p> |
- |
-<p id="bdOI_12"> |
-The assumptions that the existence of one (or two) feature(s) in a |
-javascript environment infers the existence of any feature beyond |
-the ones tested is invalid. It is only used by those ignorant of the |
-potential for diversity, imitation and the patterns of evolution in |
-browser DOMs. |
-</p> |
- |
-<p id="bdOI_13"> |
-No matter how specifically the objects from which the inferences are |
-derived are chosen, the technique itself sows the seeds of its own |
-invalidity, an object that may actually validly be used to infer that |
-a browser is of a particular type/version today probably will not still |
-be valid next year. Adding a maintenance burden to a task that already |
-presupposes an omniscient knowledge of <em>all</em> browser DOMs just |
-in order to be effectively implemented at present. |
-</p> |
- |
- |
-<h2 id="bdFD">A Strategy That Works: Object/Feature Detecting</h2> |
- |
-<p id="bdFD_1"> |
-The main point of the previous discussion is to convey the idea that it |
-is impossible to detect exactly which type of browser (or version of |
-that browser) a script is being executed on. The use that such scripts |
-have been put to in the past (to exclude browsers from sites that |
-they probably could have successfully handled) has motivated the |
-manufactures of browsers to render browser detecting nonviable |
-as a strategy for dealing with the variations in browser DOMs. |
-</p> |
- |
-<p id="bdFD_2"> |
-Fortunately, not being able to identify a web browser type or version |
-with more accuracy than could be achieved by generating a random number |
-and then biasing the result by your favourite (meaningless, because |
-they too are based on browser detecting and suffer exactly the same |
-unreliability) browser usage statistics, does not need to impact upon |
-your ability to script web browsers at all. A viable alternative |
-strategy has been identified and developed to the point where it is |
-possible to author javascript to be used on web pages without any |
-interest in the type or version of the browser at all. |
-</p> |
- |
-<p id="bdFD_3"> |
-That alternative strategy is known as object or feature detecting. I |
-prefer to use the term "feature detecting", partly because the |
-resulting code often needs to test and probe a wider range of |
-features than just those that could be described as objects, but |
-mostly because "object detecting" is occasionally |
-erroneously applied to the object inference style of script described |
-above. |
-</p> |
- |
-<p id="bdFD_4"> |
-Feature detecting seeks to match an attempt to execute as script (or a |
-part of a script) with the execution environment by seeking to test |
-features of that environment where the results of the test have a |
-direct one-to-one relationship with the features that need to be |
-supported in the environment for the code to successfully execute. It |
-is the direct one-to-one relationship in the implemented tests that |
-avoids the need to identify the specific browser because whatever |
-browser it is it either will support all of the required features or |
-it will not. That would mean testing the feature itself (to ensure |
-that it exists on the browser) and possibly aspects of the behaviour |
-of that feature. |
-</p> |
- |
-<p id="bdFD_5"> |
-Taking the previous example that illustrated how the |
-<code>ActiveXObject</code> constructor might be used as the basis for |
-a script that inferred the existence of, and ability to use, the |
-<code>clipboardData</code> feature implemented on window IE. Rather |
-than inferring the browser's support for the <code>clipboardData</code> |
-feature from some other unrelated feature it should be fairly obvious |
-that the feature that should be tested for prior to attempting to write |
-to the clipboard <em>is</em> the <code>clipboardData</code> object, and |
-further, that calling the <code>setData</code> method of that object |
-should necessitate checking that it too is implemented:- |
-</p> |
- |
-<pre id="bdFD_ex1"> |
-function copyToClip(myString){ |
- if((typeof clipboardData != 'undefined')&& |
- (clipboardData.setData)){ |
- clipboardData.setData("text",myString); |
- } |
-} |
-</pre> |
- |
-<p id="bdFD_6"> |
-In this way the tests that determine whether the |
-<code>clipboardData.setData</code> method is called have a direct |
-one-to-one relationship with the browser's support for the feature. It |
-is not necessary to be interested in whether the browser is the |
-expected windows IE that is known to implement the feature, or whether |
-it is some other browser that has decided to copy IE's implementation |
-and provide the feature itself. If the feature is there (at least to |
-the required extent) it is used and if it is not there no attempt is |
-made to use it. |
-</p> |
- |
-<p id="bdFD_7"> |
-The above feature detecting tests are done using two operations. The |
-first employs the <code>typeof</code> operator, which returns a string |
-depending on the type of its operand. That string is one of |
-<code>"undefined"</code>, <code>"object"</code>, |
-<code>"function"</code>, <code>"boolean"</code> |
-<code>"string"</code> and <code>"number"</code> |
-and the test compares the returned string with the string |
-<code>"undefined"</code>. The <code>clipboardData</code> |
-object is not used unless typeof does not return |
-<code>"undefined"</code>. |
-</p> |
- |
- |
-<p id="bdFD_8"> |
-The second test is a type-converting test. The logical AND |
-(<code>&&</code>) operator internally converts its operands to |
-boolean in order to make its decision about what value it will return. |
-If <code>clipboardData.setData</code> is undefined it will type-convert |
-to boolean <code>false</code>, while if it is an object or a function |
-the result of the conversion will be boolean <code>true</code>. |
-</p> |
- |
-<p id="bdFD_9"> |
-However, that function is not a particularly clever application of |
-feature detecting because, while it avoids the function throwing errors |
-in an attempt to execute <code>clipboardData.setData</code> on a browser |
-thatdoes not support it, it will do nothing on a browser that does not |
-support it. That is a problem when the user has been presented with a |
-GUI component that gives them the impression that their interaction |
-will result in something being written to the clipboard but when they |
-use it nothing happens. And of course nothing was going to happen if |
-the browser in use did not support javascript or it had been disabled. |
-</p> |
- |
-<p id="bdFD_10"> |
-Ensuring that a script will not attempt to use a feature that is not |
-supported is not sufficient to address the design challenge of crating |
-scripts for the Internet. Testing the browser for the features that it |
-does support makes it practical to handle a spectrum of browser DOMs |
-but the script design task also involves planning how to handle the |
-range of possibilities. A range that goes from guaranteed failure to |
-execute at all on browser that do not support javascript, to full |
-support for all of the required features. |
-</p> |
- |
-<p id="bdFD_11"> |
-You can tell when the browser does not support the |
-<code>clipboardData</code> feature from the script prior to using it |
-but the user has no way of knowing why a button that promised them |
-some action has failed to do anything. So in addition to matching the |
-script to the browser's ability to execute it, it is also necessary to |
-match the GUI, and the user's resulting expectations, to what the |
-script is going to be able to deliver. |
-</p> |
- |
-<p id="bdFD_12"> |
-Suppose the <code>copyToClip</code> function was called from an |
-<code>INPUT</code> element of <code>type="button"</code> |
-and was intended to copy a company e-mail address into the clipboard, |
-the <span class="initialism" title="HyperText Mark-up Language"><abbr title="HyperText Mark-up Language">HTML</abbr></span> code for the button might look like:- |
-</p> |
- |
-<pre id="bdFD_ex2"> |
-<input type="button" |
- value="copy our contact e-mail address to your clipboard" |
- onclick="copyToClip('info@example.com')"> |
-</pre> |
- |
-<p id="bdFD_13"> |
-We know that that <span class="initialism" title="HyperText Mark-up Language"><abbr title="HyperText Mark-up Language">HTML</abbr></span> will do nothing if javascript is |
-disabled/unavailable and we know that it will do nothing if the browser |
-does not support the required features, so one option would be to use a |
-script to write the button <span class="initialism" title="HyperText Mark-up Language"><abbr title="HyperText Mark-up Language">HTML</abbr></span> into the document in the position in |
-which the button was wanted when the browser provided the facility:- |
-</p> |
- |
-<pre id="bdFD_ex3"> |
-<script type="text/javascript"> |
- if((typeof clipboardData != 'undefined')&& |
- (clipboardData.setData)&& |
- (document.write)){ |
- document.write('<input type="button"', |
- 'value="copy our contact e-mail address', |
- ' to your clipboard" onclick="', |
- 'copyToClip(\'info@example.com\')">'); |
- } |
-</script> |
-</pre> |
- |
-<p id="bdFD_14"> |
-Now the user will never see the button unless the browser supports the |
-required features <em>and</em> javascript is enabled. The user never |
-gets an expectation that the script will not be able to deliver (at |
-least that is the theory, it is still possible for the user's browser |
-configuration to prevent scripts from writing to the clipboard, but |
-the user might be expected to know how their browser is configured and |
-understand that the button is not in a position to override it). |
-</p> |
- |
-<p id="bdFD_15"> |
-If the <code>copyToClip</code> function is only ever called from |
-buttons that are written only following the required feature detection |
-then it can be simplified by the removal of the test from its body as |
-it would be shielded from generating errors on nonsupporting browsers by |
-the fact that there would be no way for it to be executed. |
-</p> |
- |
-<p id="bdFD_16"> |
-The <code>document.write</code> method is not the only way of adding |
-GUI components to a web page in a way that can be subject to the |
-verification of the features that render those components meaningful. |
-Alternatives include writing to a parent element's |
-<code>innerHTML</code> property (where supported, see |
-<a href="/faq/#updateContent">FAQ: How do I modify the content of the current page?</a>), or using the W3C DOM |
-<code>document.crateElement</code> (or <code>createElementNS</code>) |
-methods and appending the created element at a suitable location within |
-the DOM. Either of these two approaches are suited to adding the |
-components after the page has finished loading, but that can be useful |
-as some feature testing is not practical before that point. The |
-approach used can be chosen based on the requirements of the script. |
-If the script is going to be using the |
-<code>document.createElement</code> method itself then it is a good |
-candidate as a method for inserting any required GUI components, |
-similarly with <code>innerHTML</code>. The <code>document.write</code> |
-method is universally supported in <span class="initialism" title="HyperText Mark-up Language"><abbr title="HyperText Mark-up Language">HTML</abbr></span> DOMs but is not necessarily |
-available at all in XHTML DOMs. |
-</p> |
- |
-<p id="bdFD_17"> |
-Other ways of handling the possibility that the browser will not |
-support either javascript or the features required by the script used |
-is to design the <span class="initialism" title="HyperText Mark-up Language"><abbr title="HyperText Mark-up Language">HTML</abbr></span>/<span class="initialism" title="Cascading Style Sheet"><abbr title="Cascading Style Sheet">CSS</abbr></span> parts of the page so that the script, upon |
-verifying support for the features it requires, can modify, manipulate |
-and transform the resulting elements in the DOM. But in the absence of |
-sufficient script support the unmodified <span class="initialism" title="HyperText Mark-up Language"><abbr title="HyperText Mark-up Language">HTML</abbr></span> presents all of the |
-required content, navigation, etc. |
-</p> |
- |
-<p id="bdFD_18"> |
-This can be particularly significant with things like navigation menus. |
-One style of design would place the content of the navigation menus, |
-the URLs and text, in javascript structures such as Arrays. But either |
-of javascript being disabled/unavailable on the client or the absence |
-of the features required to support a functional javascript menu would |
-leave the page without any navigation at all. Generally that would not |
-be a viable web page, and not that good for search engine placement as |
-search engine robots do not tend to execute javascript either so they |
-would be left unable to navigate a site featuring such a menu and so |
-unable to rank its content for listing. |
-</p> |
- |
-<p id="bdFD_19"> |
-A better approach to menu design would have the navigation menu content |
-defined in the <span class="initialism" title="HyperText Mark-up Language"><abbr title="HyperText Mark-up Language">HTML</abbr></span>, possibly as nested list elements of some sort, and |
-once the script has ascertained that the browser is capable of executing |
-it and providing the menu in an interactive form it can modify the <span class="initialism" title="Cascading Style Sheet"><abbr title="Cascading Style Sheet">CSS</abbr></span> |
-<code>position</code>, <code>display</code> and <code>visibility</code> |
-properties, move the elements to their desired location, attach event |
-handlers and generally get on with the task of providing a javascript |
-menu. And the worst that happens when the browser does not support |
-scripting or the required features is that the user (and any search |
-engine robots) finds the navigation in the page as a series of nested |
-lists containing links. Fully functional, if not quite as impressive as |
-it could have been had the script been supported. This is termed |
-"clean degradation" and goes hand in hand with feature |
-detecting during the process of planing and implementing a browser |
-script for the Internet. |
-</p> |
- |
-<h3 id="bdGEID">Example 1: IDed Element Retrieval</h3> |
- |
-<p id="bdGEID_1"> |
-An important aspect of feature detecting is that it allows a script to |
-take advantage of possible fall-back options. Having deduced that a |
-browser lacks the preferred feature it still may be possible to |
-achieve the desired goal by using an alternative feature that is know |
-to exist on some browsers. A common example of this is retrieving an |
-element reference from the DOM given a string representing the |
-<code>ID</code> attribute of that element. The preferred method would |
-be the W3C Core DOM standard <code>document.getElementById</code> |
-method which is supported on the widest number of browsers. If that |
-method was not available but the browser happened to support the |
-<code>document.all</code> collection then it could be used for the |
-element retrieval as a fall-back option. And for some types of |
-elements, such as <span class="initialism" title="Cascading Style Sheet"><abbr title="Cascading Style Sheet">CSS</abbr></span> positioned elements on Netscape 4 (where the |
-<code>document.layers</code> collection may be used to retrieve such |
-a reference), additional options may be available. |
-</p> |
- |
-<pre id="bdGEID_ex1"> |
-function getElementWithId(id){ |
- var obj; |
- if(document.getElementById){ |
- <span class="commentJS">/* Prefer the widely supported W3C DOM method, if |
- available:- |
- */</span> |
- obj = document.getElementById(id); |
- }else if(document.all){ |
- <span class="commentJS">/* Branch to use document.all on document.all only |
- browsers. Requires that IDs are unique to the page |
- and do not coincide with NAME attributes on other |
- elements:- |
- */</span> |
- obj = document.all[id]; |
- }else if(document.layers){ |
- <span class="commentJS">/* Branch to use document.layers, but that will only work for |
- <span class="initialism" title="Cascading Style Sheet"><abbr title="Cascading Style Sheet">CSS</abbr></span> positioned elements and LAYERs that are not nested. A |
- recursive method might be used instead to find positioned |
- elements within positioned elements but most DOM nodes on |
- document.layers browsers cannot be referenced at all. |
- */</span> |
- obj = document.layers[id]; |
- } |
- <span class="commentJS">/* If no appropriate/functional element retrieval mechanism |
- exists on this browser this function returns null:- |
- */</span> |
- return obj||null; |
-} |
-</pre> |
- |
-<p id="bdGEID_2"> |
-Although that function is not very long or complex (without its |
-comments) it does demonstrate a consequence of one style of |
-implementation of feature detecting, it repeats the test each time |
-it is necessary to retrieve an element using its ID. If not too many |
-elements need retrieving that may not be significant, but if many |
-elements needed retrieving in rapid succession and performance was |
-significant then the overhead of performing the feature detection on |
-each retrieval may add up and impact on the resulting |
-script. |
-</p> |
- |
-<p id="bdGEID_3"> |
-An alternative is to assign one of many functions to a global |
-<code>getElementWithId</code> variable based on the results of the |
-feature detecting tests, as the script loads. |
-</p> |
- |
-<pre id="bdGEID_ex2"> |
-var getElementWithId; |
-if(document.getElementById){ |
- <span class="commentJS">/* Prefer the widely supported W3C DOM method, if |
- available:- |
- */</span> |
- getElementWithId = function(id){ |
- return document.getElementById(id); |
- } |
-}else if(document.all){ |
- <span class="commentJS">/* Branch to use document.all on document.all only |
- browsers. Requires that IDs are unique to the page |
- and do not coincide with NAME attributes on other |
- elements:- |
- */</span> |
- getElementWithId = function(id){ |
- return document.all[id]; |
- } |
-}else if(document.layers){ |
- <span class="commentJS">/* Branch to use document.layers, but that will only work for <span class="initialism" title="Cascading Style Sheet"><abbr title="Cascading Style Sheet">CSS</abbr></span> |
- positioned elements. This function uses a recursive method |
- to find positioned elements within positioned elements but most |
- DOM nodes on document.layers browsers cannot be referenced at |
- all. This function is expected to be called with only one |
- argument, exactly like the over versions. |
- */</span> |
- getElementWithId = function(id, baseLayers){ |
- <span class="commentJS">/* If the - baseLayers - parameter is not provided default |
- its value to the document.layers collection of the main |
- document: |
- */</span> |
- baseLayers = baseLayers||document.layers; |
- <span class="commentJS">/* Assign the value of the property of the - baseLayers - |
- object (possibly defaulted to the document.layers |
- collection) with the property name corresponding to the |
- - id - parameter to the local variable - obj: |
- */</span> |
- var obj = baseLayers[id]; |
- <span class="commentJS">/* If - obj - remains undefined (no element existed with the |
- given - id -) try searching the indexed members of |
- - baseLayers - to see if any of their layers collections |
- contain the element with the corresponding - id: |
- */</span> |
- if(!obj){ <span class="commentJS">//Element not found</span> |
- <span class="commentJS">/* Loop through the indexed members of - baseLayers: */</span> |
- for(var c = 0;c < baseLayers.length;c++){ |
- if((baseLayers[c])&& <span class="commentJS">//Object at index - c.</span> |
- (baseLayers[c].document)&& <span class="commentJS">//It has a - document.</span> |
- <span class="commentJS">/* And a layers collection on that document: */</span> |
- (baseLayers[c].document.layers)){ |
- <span class="commentJS">/* Recursively call this function passing the - id - as |
- the first parameter and the layers collection from |
- within the document found on the layer at index - c |
- - in - baseLayers - as the second parameter and |
- assign the result to the local variable - obj: |
- */</span> |
- obj=getElementWithId(id,baseLayers[c].document.layers); |
- <span class="commentJS">/* If - obj - is now not null then we have found the |
- required element and can break out of the - for - |
- loop: |
- */</span> |
- if(obj)break; |
- } |
- } |
- } |
- <span class="commentJS">/* If - obj - will type-convert to boolean true (it is not null |
- or undefined) return it, else return null: |
- */</span> |
- return obj||null; |
- } |
-}else{ |
- <span class="commentJS">/* No appropriate element retrieval mechanism exists on |
- this browser. So assign this function as a safe dummy. |
- Values returned form calls to getElementWithId probably |
- should be tested to ensure that they are non-null prior |
- to use anyway so this branch always returns null:- |
- */</span> |
- getElementWithId = function(id){ |
- return null; |
- } |
-} |
- |
-<span class="commentJS">/* |
-Usage:- |
- |
-var elRef = getElementWithId("ID_string"); |
-if(elRef){ |
- ... //element was found |
-}else{ |
- ... //element could not be found. |
-} |
-*/</span> |
-</pre> |
- |
-<p id="bdGEID_4"> |
-The feature detecting tests are performed once as the page loads and |
-one of many function expressions assigned to the |
-<code>getElementWithId</code> global variable as a result. The |
-assigned function is the one most capable of retrieving the required |
-element on the browser in use but it is still necessary to check that |
-the returned value in not <code>null</code> and plan for the |
-possibility of failure in the element retrieval process. It is |
-guaranteed to fail on any browser that does not implement at least one |
-of the element retrieval mechanisms used as the default function just |
-returns <code>null</code>. |
-</p> |
- |
-<p id="bdGEID_5"> |
-It may not be sufficient to provide a function that does the best job |
-of element retrieval that can be done on the browser in use. Other |
-scripts, or parts of the script, may need to know how successful their |
-attempts to retrieve elements are likely to be. The |
-<code>getElementWithId</code> version that uses |
-<code>document.layers</code> cannot retrieve elements that are not <span class="initialism" title="Cascading Style Sheet"><abbr title="Cascading Style Sheet">CSS</abbr></span> |
-positioned and that may not be good enough for some scripts, while it |
-may not matter to others. The <code>document.getElementById</code> and |
-<code>document.all</code> versions should be able to retrieve any |
-element given its <code>ID</code>. An opting would be to set a couple |
-of boolean flags to indicate how successful element retrieval can be |
-expected to be. Defaulting the flags to <code>false</code> and setting |
-them to <code>true</code> in the branches that assign the various |
-function expressions. Then if other code is interested in what can be |
-expected from the <code>getElementWithId</code> function, say in order |
-to decide how to configure, or default itself, it can check the |
-pertinent flag. |
-</p> |
- |
-<pre id="bdGEID_ex3"> |
-var getElementWithId; |
-var canGetAnyElement = false; <span class="commentJS">//default any element flag</span> |
-var canGetCSSPositionedElements = false; <span class="commentJS">//default <span class="initialism" title="Cascading Style Sheet"><abbr title="Cascading Style Sheet">CSS</abbr></span> positioned flag</span> |
-if(document.getElementById){ |
- canGetAnyElement = true; <span class="commentJS">//set any element flag to true</span> |
- canGetCSSPositionedElements = true; <span class="commentJS">//set <span class="initialism" title="Cascading Style Sheet"><abbr title="Cascading Style Sheet">CSS</abbr></span> positioned flag true</span> |
- getElementWithId = ... |
-}else if(document.all){ |
- canGetAnyElement = true; <span class="commentJS">//set any element flag to true</span> |
- canGetCSSPositionedElements = true; <span class="commentJS">//set <span class="initialism" title="Cascading Style Sheet"><abbr title="Cascading Style Sheet">CSS</abbr></span> positioned flag true</span> |
- getElementWithId = ... |
-}else if(document.layers){ |
- canGetCSSPositionedElements = true; <span class="commentJS">//set <span class="initialism" title="Cascading Style Sheet"><abbr title="Cascading Style Sheet">CSS</abbr></span> positioned flag true</span> |
- <span class="commentJS">/* The - canGetAnyElement - flag is not set in this branch because |
- the document.layers collection does not make *all* elements |
- available. |
- */</span> |
- getElementWithId = ... |
-}else{ |
- <span class="commentJS">/* Neither flag is set when the dummy function is assigned because |
- it is guaranteed not to be able to retrieve any elements: |
- */</span> |
- getElementWithId = function(id){ |
- return null; |
- } |
-} |
-... |
-if(canGetCSSPositionedElements){ |
- <span class="commentJS">/* Expect to be able to retrieve <span class="initialism" title="Cascading Style Sheet"><abbr title="Cascading Style Sheet">CSS</abbr></span> positioned elements. |
- */</span> |
- ... |
- if(canGetAnyElement){ |
- <span class="commentJS">/* Expect to also be able to retrieve any other elements that |
- have an ID attribute. |
- */</span> |
- ... |
- } |
-} |
-</pre> |
- |
-<p id="bdGEID_6"> |
-The flags do not directly reflect which feature is going to be used |
-for element retrieval, instead they reflect what can be expected from |
-the <code>getElementWithId</code> function on the current browser. |
-Allowing a script that requires a particular level of performance |
-(say the retrieval of any element) to determine whether it will have |
-that facility but without denying the facility from a script with a |
-less demanding requirement. |
-</p> |
- |
-<h3 id="bdScroll">Example 2: Scroll Values</h3> |
- |
-<p id="bdScroll_1"> |
-Another common task that needs to be approached differently on |
-different browsers is the retrieval of the extent to which the user |
-has scrolled a web page. The majority of browsers provide properties |
-of the global object called <code>pageXOffset</code> and |
-<code>pageYOffset</code>, which hold the relevant values. Some make the |
-equivalent browsers available as scrollLeft and scrollTop properties on |
-the "root" element (either in addition to the |
-<code>pageX/YOffset</code> properties or instead of them). The task is |
-complicated further by the fact that which element is the |
-"root" element depends on various factors, it was always the |
-<code>document.body</code> element in the past but newer (<span class="initialism" title="Cascading Style Sheet"><abbr title="Cascading Style Sheet">CSS</abbr></span>) |
-standards compliant browsers (and browsers that can operate in various |
-modes, including standards compliant mode) make the |
-<code>document.documentElement</code> the root element. Then there may |
-be browsers that do not make the scrolling values available at all. |
-</p> |
- |
-<p id="bdScroll_2"> |
-Because the <code>pageXOffset</code> and <code>pageYOffset</code> |
-properties are implemented on the largest number of browsers, and their |
-use avoids the need to worry about the "root" element, they |
-are the preferred values to use. In there absence the problem moves on |
-to identifying the "root" element, which is made easier by |
-the browsers that understand standards compliant mode and provide a |
-<code>document.compatMode</code> string property to announce which mode |
-they are in. If the string property is missing or the value of that |
-string is other than <code>"CSS1Compat"</code> then it is the |
-<code>document.body</code> object that needs to be read for the |
-scrolling values, else the <code>document.documentElement</code> should |
-be read. Testing for the presence of any of the scroll values |
-themselves needs to done with a <code>typeof</code> test because they |
-are numeric values and if implemented but set to zero a type-converting |
-test would return <code>false</code> but that would not be an indicator |
-of their absence. |
-</p> |
- |
-<p id="bdScroll_3"> |
-The following is an example that employs feature detection to decide |
-which scroll values to read:- |
-</p> |
- |
-<pre id="bdScroll_ex1"> |
-<span class="commentJS">/* The - getPageScroll - global variable is assigned a reference to a |
- function and when that function is called initially it configures |
- the script to read the correct values, if available, and then |
- returns a reference to the object - interface - which provides |
- methods that retrieve the scroll values. Subsequent invocations of |
- the getPageScroll function do not repeat the configuration, they |
- just return a reference to the same interface object. Because the |
- configuration stage may need to check whether the document.body |
- element exists the function cannot be called until the browser has |
- parsed the opening body tag as prior to that point there is no |
- document.body element. |
- |
- Usage:- |
- var scrollInterface = getPageScroll(); |
- var scrollX = scrollInterface.getScrollX(); |
- var scrollY = scrollInterface.getScrollY(); |
- |
- The interface methods return NaN if the browser provides no method |
- of reading the scroll values. A returned NaN value can be tested for |
- with the - isNaN - global function, but it should not be necessary |
- to perform the isNaN test on more than the first retrieval because |
- if the returned value is NaN it will always be NaN and if it is not |
- it should never be. |
- |
- if(isNaN(scrollX)){ |
- //No scroll value retrieval mechanism was available on this browser |
- } |
- |
- (* The script performs an inline execution of a function expression |
- which returns the function object that is assigned to the - |
- getPageScroll - global variable. This produces a closure that |
- preserves the local variables of the executed function expression, |
- allowing the execution context of the function expression to provide |
- a repository for the configuration results, keeping them out of the |
- global namespace. The format is:- |
- |
- v--- Anonymous function expression --v |
- var variable = (function(){ ...; return returnValue; })(); |
- Inline execution of the function expression ----^^ |
- |
- The value returned by the inline execution of the anonymous function |
- expression is assigned to the variable. If that returned value is a |
- reference to an inner function object then the assignment will form |
- a closure.) |
-*/</span> |
-var getPageScroll = (function(){ |
- <span class="commentJS">/* The local variable "global" is assigned the value - this - |
- because the function expression is executing in the global |
- context and - this - refers to the global object in that |
- context. The global object is usually the - window - object on |
- web browsers but this local variable is going to be used in the |
- configuration tests for convenience: |
- */</span> |
- var global = this; |
- <span class="commentJS">/* notSetUp - Is a flag to indicate when the script has done the |
- setup configuration: |
- */</span> |
- var notSetUp = true; |
- <span class="commentJS">/* readScroll - Is initially a dummy object that is used to return |
- the NaN values whenever no functional scroll value retrieval |
- mechanism is available on the browser. It is assigned a |
- reference to the object from which the scroll values can be read |
- if the feature detection determines that to be possible: |
- */</span> |
- var readScroll = {scrollLeft:NaN,scrollTop:NaN}; |
- <span class="commentJS">/* Using the local variables - readScrollX - and readScrollY - to |
- hold the property names allows the same functions to read both |
- the pageX/YOffset properties of the global object and the |
- scrollTop/Left properties of the "root" element by assigning |
- different values to the variables. These are the defaults: |
- */</span> |
- var readScrollX = 'scrollLeft'; |
- var readScrollY = 'scrollTop'; |
- <span class="commentJS">/* The - itrface - local variable is assigned a reference to an |
- object and it is this object that is returned whenever |
- getPageScroll() is called. The object has two properties, |
- getScrollX and getScrollY, which are assigned the values of two |
- anonymous function expressions. These functions are inner |
- functions and as a result have access to the local variables of |
- the function that contains them (the anonymous function |
- expression that is executed inline in order to assign value to |
- the getPageScroll global variable). The use a square bracket |
- property accessor to read a value of whatever object has been |
- assigned to the variable - readScroll - with a property name |
- that corresponds to the value assigned to whichever of the |
- variables - readScrollX - or - readScrollY - are employed, |
- allows the functions to use the simplest code poible to provide |
- values for all of the possible permutations resting from the |
- feature detection derived configuration: |
- */</span> |
- var itrface = { |
- getScrollX:function(){ |
- return readScroll[readScrollX]; |
- }, |
- getScrollY:function(){ |
- return readScroll[readScrollY]; |
- } |
- }; |
- <span class="commentJS">/* The - setUp - inner function is called to perform the feature |
- detection and configure the variables that will be employed in |
- reading the correct scroll values. It sets the - notSetUp - flag |
- to false once it has been executed so that configuration only |
- happens the first time that a request for the interface object |
- is made: |
- */</span> |
- function setUp(){ |
- <span class="commentJS">/* As the paeX/YOffset properties are the preferred values to |
- use they are tested for first. They are not both tested |
- because if one exists the other can be assumed to exist for |
- symmetry. The testing method is a - typeof - test to see if |
- the value is a number. A type-converting test cannot be used |
- because the number zero would result in boolean false and a |
- pageXOffset value will be zero if the page has not been |
- scrolled: |
- */</span> |
- if(typeof global.pageXOffset == 'number'){ |
- <span class="commentJS">/* If pageXOffset is a number then the value of the - |
- global - variable (assigned a reference to the global |
- object earlier) is assigned to the - readScroll - |
- variable and the strings "pageXOffset" and "pageYOffset" |
- are assigned to the - readScrollX - and - readScrollY - |
- variables so they will be the property names used to |
- access the - readScroll- (now the global) object. |
- */</span> |
- readScroll = global; |
- readScrollY = 'pageYOffset'; |
- readScrollX = 'pageXOffset'; |
- }else{ |
- <span class="commentJS">/* If pageXOffset is undefined it is time to find out which |
- object is the "root" element. First, does the browser |
- have a - document.compatMode - string, if it does then |
- is its value "BackCompat", "QuirksMode" or "CSS1Compat". |
- Instead of comparing the string directly it is searched |
- for the substring "<span class="initialism" title="Cascading Style Sheet"><abbr title="Cascading Style Sheet">CSS</abbr></span>" which might make the script more |
- robust in the face of possible future "CSSnCompat" |
- modes, which are unlikely to demand that the "root" |
- element is moved again. |
- |
- The tests also verifies that there is a - |
- document.documentElement - to read and that its |
- - scrollLeft - property is a number: |
- */</span> |
- if((typeof document.compatMode == 'string')&& |
- (document.compatMode.indexOf('CSS') >= 0)&& |
- (document.documentElement)&& |
- (typeof document.documentElement.scrollLeft=='number')){ |
- <span class="commentJS">/* The - readScrollX - and - readScrollY - variables |
- are already defaulted to the required strings so it |
- is only necessary to assign a reference to the - |
- document.documentElement - to the - readScroll - |
- variable: |
- */</span> |
- readScroll = document.documentElement; |
- <span class="commentJS">/* If the browser is not in the appropriate mode the scroll |
- values should be read from the document.body - element, |
- assuming it exists on this browser and that the |
- - scrollLeft - property is a number: |
- */</span> |
- }else if((document.body)&& |
- (typeof document.body.scrollLeft == 'number')){ |
- <span class="commentJS">/* The - readScrollX - and - readScrollY - variables |
- are already defaulted to the required strings so it |
- is only necessary to assign a reference to the - |
- document.body - to the - readScroll - variable: |
- */</span> |
- readScroll = document.body; |
- } |
- <span class="commentJS">/* No other scroll value reading options exist so if - |
- readScroll - has not been assigned a new value by this |
- point it will remain a reference to the object with the |
- NaN value properties. |
- */</span> |
- } |
- notSetUp = false; <span class="commentJS">//No need to repeat configuration.</span> |
- } |
- <span class="commentJS">/* The inline execution of the anonymous function expression |
- returns with the following statement. It returns an inner |
- function expression and it is that function that will be called |
- when getPageScroll() is executed. Doing this forms a closure, |
- preserving all of the local variables and functions defined |
- within the executed anonymous function expression. Calling that |
- returned function as - getPageScroll() - executes the setUp |
- function, but only if it has not already been called, and |
- returns a reference to the - itrface - object. |
- */</span> |
- return (function(){ |
- if(notSetUp){ <span class="commentJS">//If the - notSetUp - variable is still true.</span> |
- setUp(); <span class="commentJS">//Execute the - setUp - function.</span> |
- } |
- return itrface; <span class="commentJS">//returns a reference to - itrface</span> |
- }); |
-})(); <span class="commentJS">//inline-execution of anonymous function expression, one-off!</span> |
-</pre> |
- |
-<p id="bdScroll_4"> |
-The effect of this code is to match the browser's ability to provide |
-the scrolling information with a script's desire to read it through a |
-simple and efficient interface that acts based on the results of |
-featured detecting tests that it applies only once, and if the browser |
-does not support any methods of reading the scroll values the return |
-values form the interface method indicate that fact by being NaN. It |
-does not matter that Netscape 4 will be reading the global |
-<code>pageX/YOffset</code> properties, that IE 4 will read the |
-<code>scrollTop/Left</code> properties from <code>document.body</code> |
-or that IE 6 will read those values from one of two possible objects |
-based on the <code>document.compatMode</code> value. What is important |
-is that if unknown browser XYZ provides any of those mechanisms for |
-reporting the scroll values the script is going to be able to use them, |
-without ever knowing (or caring) that it is browser XYZ that it is |
-executing on. |
-</p> |
- |
-<h3 id="bdReplace">Example 3: String.prototype.replace</h3> |
- |
-<p id="bdReplace_1"> |
-Feature detecting is not restricted to features of the DOM, it can be |
-extended to include features of the javascript language implementation. |
-For example the <code>String.prototype.replace</code> function in later |
-language versions will accept a function reference as its second |
-argument, while earlier versions only accept a string in that context. |
-Code that wants to use the function argument facility of the |
-<code>replace</code> method will fail badly if it is not supported on |
-the browser. |
-</p> |
- |
-<p id="bdReplace_2"> |
-As usual, a feature-detecting test for the implementation's ability to |
-support function arguments with the <code>replace</code> method has to |
-be a direct test on that feature. The following example test takes |
-advantage of the fact that a browser that only supports the string |
-argument version of <code>replace</code> will type-convert a function |
-reference used in that context into a string. The <code>replace</code> |
-method uses a Regular Expression (its first argument) to |
-identify parts of a string and then replaces them with a string that is |
-provided as its second argument. If the second argument is a function, |
-and the browser supports the function argument, that function is called |
-and its return value replaces the identified parts of the string. |
-</p> |
- |
-<p id="bdReplace_3"> |
-By providing a function expression that returns an empty string as its |
-second argument and a Regular Expression that identifies the entire |
-original string as the first argument, the operation of the |
-<code>replace</code> method will result in an empty string if the |
-function argument is supported. But if only string arguments are |
-supported then the function will be type-converted into a string and |
-that string will not be an empty string so the result of the |
-evaluation of the <code>replace</code> method will not be an empty |
-string. Applying a NOT (<code>!</code>) operation to the resulting |
-string type-converts the empty string into a boolean value, inverts |
-it and returns <code>true</code>, the non-empty string would result |
-in <code>false</code>. |
-</p> |
- |
-<pre id="bdReplace_ex1"> |
-<span class="commentJS">/* The original string is the one letter string literal "a". The |
- Regular Expression /a/ identifies that entire string, so it is the |
- entire original string that will be replaced. The second argument is |
- the function expression - function(){return '';} -, so the entire |
- original string will be replaced with an empty string if the |
- function expression is executed. If it is instead type-converted |
- into a string that string will not be an empty string. The NOT (!) |
- operation type-converts its operand to boolean and then inverts it |
- so the results of the test is boolean true if function references |
- are supported in the second argument for the - replace - method, and |
- false when not supported: |
-*/</span> |
-if(!('a'.replace(/a/, (function(){return '';})))){ |
- ... <span class="commentJS">//function references OK.</span> |
-}else{ |
- ... <span class="commentJS">//no function references with replace.</span> |
-} |
-</pre> |
- |
-<p id="bdReplace_4"> |
-The common thread with feature detecting is that it is the code that is |
-going to use the features, and the nature of those features, that |
-defines how support for the required feature needs to be tested. Once |
-you get used to the idea it starts to become obvious which tests need |
-to be applied to verify a browser's support for a feature, and then it |
-is time to work on the efficient application of feature detection. |
-</p> |
- |
-<h2 id="bdDesPb">The Javascript Design Problem</h2> |
- |
-<p id="bdDesPb_1"> |
-Javascript as a language is not that complex, it may have its quirks |
-but it can be defined entirely in the 173 pages of the ECMA |
-specification (3rd edition). The challenge of authoring javascript |
-comes form the diversity of execution environments. When authoring for |
-the Internet nothing is known about the receiving software in advance, |
-and even when that software is a web browser that will execute |
-javascript, there is still a spectrum of possible DOM implementations |
-to contend with. |
-</p> |
- |
-<p id="bdDesPb_2"> |
-The combination of the facts that it is impossible to determine which |
-browser is executing the script, and that it is impossible to be |
-familiar with all browser DOMs can be rendered insignificant by using |
-feature detection to match code execution with any browser's ability to |
-support it. But there is still going to be a diversity of |
-outcomes, ranging from total failure to execute any scripts (on |
-browsers that do not support javascript, or have it disabled) to full |
-successful execution on the most capable javascript enabled browsers. |
-</p> |
- |
-<p id="bdDesPb_3"> |
-The challenge when designing scripts is to cope with all of the |
-possibilities in a way that makes sense for everyone. As those |
-possibilities will always include browsers incapable of executing |
-javascript at all, the starting point must be pages based on (valid) |
-<span class="initialism" title="HyperText Mark-up Language"><abbr title="HyperText Mark-up Language">HTML</abbr></span> that contain all of the required content, allow the necessary |
-navigation and are as functional as they purport to be (possibly with |
-the backing of server-side scripting, which does not have any of the |
-problems of client side scripting). On top of that reliable foundation |
-it is possible to layer the scripts. Feature detecting and adding |
-scripted enhancements when the browser is capable of supporting them, |
-cleanly degrading to the underlying and reliable <span class="initialism" title="HyperText Mark-up Language"><abbr title="HyperText Mark-up Language">HTML</abbr></span> when it is not. |
-</p> |
- |
-<p id="bdDesPb_4"> |
-A well designed script, implementing a suitable strategy, can enhance |
-the underlying <span class="initialism" title="HyperText Mark-up Language"><abbr title="HyperText Mark-up Language">HTML</abbr></span> page, exploiting the browser's capabilities to the |
-maximum extent possible and still exhibit planed behaviour in the |
-absence of any (or all) desired features and degrade cleanly where |
-necessary. Nobody should either consider themselves a skilled Internet |
-javascript author, or deprecate javascript as a language and/or browser |
-scripting as a task, until they have demonstrated an ability to write |
-a non-trivial script that achieves that goal. |
-</p> |
-</body> |
-</html> |
/cljs/faq_notes/not_browser_detect.html |
Property changes: |
Deleted: svn:mime-type |
## -1 +0,0 ## |
-text/plain |
\ No newline at end of property |
Index: cljs/faq_notes/closures.html |
=================================================================== |
--- cljs/faq_notes/closures.html (revision 43) |
+++ cljs/faq_notes/closures.html (nonexistent) |
@@ -1,1574 +0,0 @@ |
-<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" |
- "http://www.w3.org/TR/html4/strict.dtd"> |
-<html lang="en"> |
-<head> |
-<title>Javascript Closures</title> |
-<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1"> |
-<link href="../../faq.css" rel="stylesheet" type="text/css"> |
-<link href="../notes.css" rel="stylesheet" type="text/css"> |
-<style type="text/css"> |
-CODE { white-space:nowrap; } |
-.scopeCh { |
- white-space:nowrap; |
- font-family:Courier, monospace; |
-} |
-</style> |
-</head> |
-<body> |
- |
-<h1>Javascript Closures</h1> |
-<div id="faqNav"> |
- <a href="../../">FAQ</a> > <a href="../">FAQ Notes</a> |
-</div> |
- |
-<ul> |
- <li><a href="#clIntro">Introduction</a></li> |
- <li><a href="#clResO">The Resolution of Property Names on Objects</a> |
- <ul> |
- <li><a href="#clResA">Assignment of Values</a></li> |
- <li><a href="#clResR">Reading of Values</a></li> |
- </ul> |
- </li> |
- <li><a href="#clIRExSc">Identifier Resolution, Execution Contexts and scope chains</a> |
- <ul> |
- <li><a href="#clExCon">The Execution Context</a></li> |
- <li><a href="#clScCh">scope chains and [[scope]]</a></li> |
- <li><a href="#clIdRes">Identifier Resolution</a></li> |
- </ul> |
- </li> |
- <li><a href="#clClose">Closures</a> |
- <ul> |
- <li><a href="#clAtGb">Automatic Garbage Collection</a></li> |
- <li><a href="#clFrmC">Forming Closures</a></li> |
- </ul> |
- </li> |
- <li><a href="#clClDo">What can be done with Closures?</a> |
- <ul> |
- <li><a href="#clSto">Example 1: setTimeout with Function References</a></li> |
- <li><a href="#clObjI">Example 2: Associating Functions with Object Instance Methods</a></li> |
- <li><a href="#clEncap">Example 3: Encapsulating Related Functionality</a></li> |
- <li><a href="#clOtE">Other Examples</a></li> |
- </ul> |
- </li> |
- <li><a href="#clAc">Accidental Closures</a></li> |
- <li><a href="#clMem">The Internet Explorer Memory Leak Problem</a></li> |
-</ul> |
- |
-<h2 id="clIntro">Introduction</h2> |
- |
-<blockquote cite="http://groups.google.com/groups?selm=wu535hos.fsf@hotpop.com"> |
- <dl> |
- <dt id="clDefN">Closure</dt> |
- <dd>A "closure" is an expression (typically a function) that |
- can have free variables together with an environment that binds |
- those variables (that "closes" the expression). |
- <dd> |
- </dl> |
-</blockquote> |
- |
-<p> |
-Closures are one of the most powerful features of ECMAScript |
-(javascript) but they cannot be property exploited without |
-understanding them. They are, however, relatively easy to create, |
-even accidentally, and their creation has potentially harmful |
-consequences, particularly in some relatively common web browser |
-environments. To avoid accidentally encountering the drawbacks and |
-to take advantage of the benefits they offer it is necessary to |
-understand their mechanism. This depends heavily on the role of |
-scope chains in identifier resolution and so on the resolution of |
-property names on objects. |
-</p> |
- |
-<p> |
-The simple explanation of a Closure is that ECMAScript allows inner |
-functions; function definitions and function expressions that are |
-inside the function bodes of other functions. And that those inner |
-functions are allowed access to all of the local variables, parameters |
-and declared inner functions within their outer function(s). A closure |
-is formed when one of those inner functions is made accessible outside |
-of the function in which it was contained, so that it may be executed |
-after the outer function has returned. At which point it still has |
-access to the local variables, parameters and inner function |
-declarations of its outer function. Those local variables, parameter |
-and function declarations (initially) have the values that they had |
-when the outer function returned and may be interacted with by the |
-inner function. |
-</p> |
- |
-<p> |
-Unfortunately, properly understanding closures requires an |
-understanding of the mechanism behind them, and quite a bit of |
-technical detail. While some of the ECMA 262 specified algorithms have |
-been brushed over in the early part of the following explanation, much |
-cannot be omitted or easily simplified. Individuals familiar with |
-object property name resolution may skip that section but only people |
-already familiar with closures can afford to skip the following |
-sections, and they can stop reading now and get back to exploiting |
-them. |
-</p> |
- |
-<h2 id="clResO">The Resolution of Property Names on Objects</h2> |
- |
-<p> |
-ECMAScript recognises two categories of object, "Native Object" |
-and "Host Object" with a sub-category of native objects called |
-"Built-in Object" (ECMA 262 3rd Ed Section 4.3). Native objects |
-belong to the language and host objects are provided by the environment, |
-and may be, for example, document objects, DOM nodes and the like. |
-</p> |
- |
-<p> |
-Native objects are loose and dynamic bags of named properties (some |
-implementations are not that dynamic when it comes to the built in |
-object sub-category, though usually that doesn't matter). The defined |
-named properties of an object will hold a value, which may be a |
-reference to another Object (functions are also Objects in this sense) |
-or a primitive value: String, Number, Boolean, Null or Undefined. The |
-Undefined primitive type is a bit odd in that it is possible to assign |
-a value of Undefined to a property of an object but doing so does not |
-remove that property from the object; it remains a defined named |
-property, it just holds the value <code>undefined</code>. |
-</p> |
- |
-<p> |
-The following is a simplified description of how property values are |
-read and set on objects with the internal details brushed over to the |
-greatest extent possible. |
-</p> |
- |
-<h3><a name="clResA" id="clResA">Assignment of Values</a></h3> |
- |
-<p> |
-Named properties of objects can be created, or values set on existing |
-named properties, by assigning a value to that named property. So |
-given:- |
-</p> |
- |
-<pre> |
-var objectRef = new Object(); <span class="commentJS">//create a generic javascript object.</span> |
-</pre> |
- |
-<p> |
-A property with the name "testNumber" can be created as:- |
-</p> |
- |
-<pre> |
-objectRef.testNumber = 5; |
-<span class="commentJS">/* - or:- */</span> |
-objectRef["testNumber"] = 5; |
-</pre> |
- |
-<p> |
-The object had no "testNumber" property prior to the |
-assignment but one is created when the assignment is made. Any |
-subsequent assignment does not need to create the property, it just |
-re-sets its value:- |
-</p> |
- |
-<pre> |
-objectRef.testNumber = 8; |
-<span class="commentJS">/* - or:- */</span> |
-objectRef["testNumber"] = 8; |
-</pre> |
- |
-<p> |
-Javascript objects have prototypes that can themselves be objects, as |
-will be described shortly, and that prototype may have named |
-properties. But this has no role in assignment. If a value is assigned |
-and the actual object does not have a property with the corresponding |
-name a property of that name is created and the value is assigned to |
-it. If it has the property then its value is re-set. |
-</p> |
- |
-<h3><a name="clResR" id="clResR">Reading of Values</a></h3> |
- |
-<p> |
-It is in reading values from object properties that prototypes come |
-into play. If an object has a property with the property name used in |
-the property accessor then the value of that property is returned:- |
-</p> |
- |
-<pre> |
-<span class="commentJS">/* Assign a value to a named property. If the object does not have a |
- property with the corresponding name prior to the assignment it |
- will have one after it:- |
-*/</span> |
-objectRef.testNumber = 8; |
- |
-<span class="commentJS">/* Read the value back from the property:- */</span> |
- |
-var val = objectRef.testNumber; |
-<span class="commentJS">/* and - val - now holds the value 8 that was just assigned to the |
- named property of the object. */</span> |
- </pre> |
- |
-<p> |
-But all objects may have prototypes, and prototypes are objects so they, in |
-turn, may have prototypes, which may have prototypes, and so on forming |
-what is called the prototype chain. The prototype chain ends when one |
-of the objects in the chain has a null prototype. The default prototype for the |
-<code>Object</code> constructor has a null prototype so:- |
-</p> |
- |
-<pre> |
-var objectRef = new Object(); <span class="commentJS">//create a generic javascript object.</span> |
-</pre> |
- |
-<p> |
-Creates an object with the prototype <code>Object.prototype</code> that itself has a |
-null prototype. So the prototype chain for <code>objectRef</code> contains only one |
-object: <code>Object.prototype</code>. However:- |
-</p> |
- |
-<pre> |
-<span class="commentJS">/* A "constructor" function for creating objects of a - |
- MyObject1 - type. |
-*/</span> |
-function MyObject1(formalParameter){ |
- <span class="commentJS">/* Give the constructed object a property called - testNumber - and |
- assign it the value passed to the constructor as its first |
- argument:- |
- */</span> |
- this.testNumber = formalParameter; |
-} |
- |
-<span class="commentJS">/* A "constructor" function for creating objects of a - |
- MyObject2 - type:- |
-*/</span> |
-function MyObject2(formalParameter){ |
- <span class="commentJS">/* Give the constructed object a property called - testString - |
- and assign it the value passed to the constructor as its first |
- argument:- |
- */</span> |
- this.testString = formalParameter; |
-} |
- |
-<span class="commentJS">/* The next operation replaces the default prototype associated with |
- all MyObject2 instances with an instance of MyObject1, passing the |
- argument - 8 - to the MyObject1 constructor so that its - |
- testNumber - property will be set to that value:- |
-*/</span> |
-MyObject2.prototype = new MyObject1( 8 ); |
- |
-<span class="commentJS">/* Finally, create an instance of - MyObject2 - and assign a reference |
- to that object to the variable - objectRef - passing a string as the |
- first argument for the constructor:- |
-*/</span> |
- |
-var objectRef = new MyObject2( "String_Value" ); |
-</pre> |
- |
-<p> |
-The instance of <code>MyObject2</code> referred to by the <code>objectRef</code> variable has a |
-prototype chain. The first object in that chain is the instance of |
-<code>MyObject1</code> that was created and assigned to the prototype |
-property of the <code>MyObject2</code> constructor. The instance of |
-<code>MyObject1</code> has a prototype, the object that was assigned to the function |
-<code>MyObject1</code>'s prototype property by the implementation. That object has |
-a prototype, the default <code>Object</code> prototype that corresponds with the |
-object referred to by <code>Object.prototype</code>. <code>Object.prototype</code> has a null |
-prototype so the prototype chain comes to an end at this point. |
-</p> |
-<p> |
-When a property accessor attempts to read a named property form the |
-object referred to by the variable <code>objectRef</code> the whole |
-prototype chain can enter into the process. In the simple case:- |
-</p> |
- |
-<pre> |
-var val = objectRef.testString; |
-</pre> |
- |
-<p> |
-- the instance of <code>MyObject2</code> referred to by <code>objectRef</code> has |
-a property with the name "testString" so it is the value of |
-that property, set to "String_Value", that is assigned to the |
-variable <code>val</code>. However:- |
-</p> |
- |
-<pre> |
-var val = objectRef.testNumber; |
-</pre> |
- |
-<p> |
-- cannot read a named property form the instance of |
-<code>MyObject2</code> itself as it has no such property but the |
-variable <code>val</code> is set to the value of <code>8</code> rather |
-than undefined because having failed to find a corresponding named |
-property on the object itself the interpreter then examines the object |
-that is its prototype. Its prototype is the instance of |
-<code>MyObject1</code> and it was created with a property named |
-"testNumber" with the value <code>8</code> assigned to that property, so |
-the property accessor evaluates as the value <code>8</code>. Neither |
-<code>MyObject1</code> or <code>MyObject2</code> have defined a |
-<code>toString</code> method, but if a property accessor attempts to |
-read the value of a <code>toString</code> property from |
-<code>objectRef</code>:- |
-</p> |
- |
-<pre> |
-var val = objectRef.toString; |
-</pre> |
- |
-<p> |
-- the <code>val</code> variable is assigned a reference to a function. |
-That function is the <code>toString</code> property of |
-<code>Object.prototype</code> and is returned because the process of |
-examining the prototype of <code>objectRef</code>, when |
-<code>objectRef</code> turns out not to have a "toString" |
-property, is acting on an object, so when that prototype is found to |
-lack the property its prototype is examined in turn. Its prototype |
-is <code>Object.prototype</code>, which does have a |
-<code>toString</code> method so it is a reference to that function |
-object that is returned. |
-</p> |
- |
-<p> |
-Finally:- |
-</p> |
- |
-<pre> |
-var val = objectRef.madeUpProperty; |
-</pre> |
- |
-<p> |
-- returns <code>undefined</code>, because as the process of working up the prototype |
-chain finds no properties on any of the object with the name |
-"madeUpPeoperty" it eventually gets to the prototype of |
-<code>Object.prototype</code>, which is null, and the process ends |
-returning <code>undefined</code>. |
-</p> |
- |
-<p> |
-The reading of named properties returns the first value found, on the |
-object or then from its prototype chain. The assigning of a value to a |
-named property on an object will create a property on the object itself |
-if no corresponding property already exists. |
-</p> |
- |
-<p> |
-This means that if a value was assigned as |
-<code>objectRef.testNumber = 3</code> a "testNumber" property |
-will be created on the instance of <code>MyObject2</code> itself, and |
-any subsequent attempts to read that value will retrieve that value as |
-set on the object. The prototype chain no longer needs to be examined |
-to resolve the property accessor, but the instance of |
-<code>MyObject1</code> with the value of <code>8</code> assigned to its |
-"testNumber" property is unaltered. The assignment to the |
-<code>objectRef</code> object masks the corresponding property in its |
-prototype chain. |
-</p> |
- |
-<p> |
-Note: ECMAScript defines an internal <code>[[prototype]]</code> |
-property of the internal Object type. This property is not directly accessible with |
-scripts, but it is the chain of objects referred to with the |
-internal <code>[[prototype]]</code> property that is used in property |
-accessor resolution; the object's prototype chain. A public |
-<code>prototype</code> property exists to allow the assignment, |
-definition and manipulation of prototypes in association with the |
-internal <code>[[prototype]]</code> property. The details of the |
-relationship between to two are described in ECMA 262 (3rd edition) |
-and are beyond the <dfn>scope</dfn> of this discussion. |
-</p> |
- |
-<h2 id="clIRExSc">Identifier Resolution, Execution Contexts and scope chains</h2> |
- |
-<h3 id="clExCon">The Execution Context</h3> |
- |
-<p> |
-An <dfn>execution context</dfn> is an abstract concept used by the ECMSScript |
-specification (ECMA 262 3rd edition) to define the behaviour required |
-of ECMAScript implementations. The specification does not say anything |
-about how <dfn>execution contexts</dfn> should be implemented but execution |
-contexts have associated attributes that refer to specification defined |
-structures so they might be conceived (and even implemented) as objects |
-with properties, though not public properties. |
-</p> |
- |
-<p> |
-All javascript code is executed in an <dfn>execution context</dfn>. Global code |
-(code executed inline, normally as a JS file, or <span class="initialism" title="HyperText Mark-up Language"><abbr title="HyperText Mark-up Language">HTML</abbr></span> page, loads) gets |
-executed in <dfn>global</dfn> <dfn>execution context</dfn>, and each invocation of a function (possibly as a constructor) has an associated |
-<dfn>execution context</dfn>. Code executed with the <code>eval</code> function |
-also gets a distinct execution context but as <code>eval</code> is |
-never normally used by javascript programmers it will not be discussed |
-here. The specified details of <dfn>execution contexts</dfn> are to be found in |
-section 10.2 of ECMA 262 (3rd edition). |
-</p> |
- |
-<p> |
-When a javascript function is called it enters an <dfn>execution context</dfn>, |
-if another function is called (or the same function recursively) a new |
-<dfn>execution context</dfn> is created and execution enters that context for the |
-duration of the function call. Returning to the original execution |
-context when that called function returns. Thus running javascript code |
-forms a stack of <dfn>execution contexts</dfn>. |
-</p> |
- |
-<p> |
-When an <dfn>execution context</dfn> is created a number of things happen in a |
-defined order. First, in the <dfn>execution context</dfn> of a function, an |
-"Activation" object is created. The activation object is |
-another specification mechanism. It can be considered as an object |
-because it ends up having accessible named properties, but it is not a |
-normal object as it has no prototype (at least not a defined prototype) |
-and it cannot be directly referenced by javascript code. |
-</p> |
- |
-<p> |
-The next step in the creation of the <dfn>execution context</dfn> for a function |
-call is the creation of an <code>arguments</code> object, which is an |
-array-like object with integer indexed members corresponding with the |
-arguments passed to the function call, in order. It also has |
-<code>length</code> and <code>callee</code> properties (which are not |
-relevant to this discussion, see the spec for details). A property of |
-the Activation object is created with the name "arguments" |
-and a reference to the <code>arguments</code> object is assigned to |
-that property. |
-</p> |
- |
-<p> |
-Next the <dfn>execution context</dfn> is assigned a <dfn>scope</dfn>. A <dfn>scope</dfn> consists of a |
-list (or chain) of objects. Each function object has an internal |
-<code>[[scope]]</code> property (which we will go into more detail |
-about shortly) that also consists of a list (or chain) of objects. |
-The <dfn>scope</dfn> that is assigned to the <dfn>execution context</dfn> of a function call |
-consists of the list referred to by the <code>[[scope]]</code> property |
-of the corresponding function object with the Activation object added |
-at the front of the chain (or the top of the list). |
-</p> |
- |
-<p> |
-Then the process of "variable instantiation" takes place using an object |
-that ECMA 262 refers to as the "Variable" object. However, |
-the Activation object is used as the Variable object (note this, it is |
-important: they are the same object). Named properties of the Variable |
-object are created for each of the function's formal parameters, and if |
-arguments to the function call correspond with those parameters the |
-values of those arguments are assigned to the properties (otherwise the |
-assigned value is <code>undefined</code>). Inner function definitions |
-are used to create function objects which are assigned to properties of |
-the Variable object with names that correspond to the function name |
-used in the function declaration. The last stage of variable |
-instantiation is to create named properties of the Variable object |
-that correspond with all the local variables declared within the |
-function. |
-</p> |
- |
-<p> |
-The properties created on the Variable object that correspond with |
-declared local variables are initially assigned <code>undefined</code> |
-values during variable instantiation, the actual initialisation of |
-local variables does not happen until the evaluation of the |
-corresponding assignment expressions during the execution of the |
-function body code. |
-</p> |
- |
-<p> |
-It is the fact that the Activation object, with its |
-<code>arguments</code> property, and the Variable object, with named |
-properties corresponding with function local variables, are the same |
-object, that allows the identifier <code>arguments</code> to be treated |
-as if it was a function local variable. |
-</p> |
- |
-<p> |
-Finally a value is assigned for use with the <code>this</code> keyword. |
-If the value assigned refers to an object then property accessors |
-prefixed with the <code>this</code> keyword reference properties of |
-that object. If the value assigned (internally) is null then the |
-<code>this</code> keyword will refer to the global object. |
-</p> |
- |
-<p> |
-The global execution context gets some slightly different handling as |
-it does not have arguments so it does not need a defined Activation |
-object to refer to them. The global execution context does need a <dfn>scope</dfn> |
-and its <dfn>scope chain</dfn> consists of exactly one object, the global object. |
-The global execution context does go through variable instantiation, |
-its inner functions are the normal top level function declarations that |
-make up the bulk of javascript code. The global object is used as the |
-Variable object, which is why globally declared functions become |
-properties of the global object. As do globally declared variables. |
-</p> |
- |
-<p> |
-The global execution context also uses a reference to the global object |
-for the <code>this</code> object. |
-</p> |
- |
-<h3 id="clScCh">scope chains and [[scope]]</h3> |
- |
-<p> |
-The <dfn>scope chain</dfn> of the execution context for a function call is |
-constructed by adding the execution context's Activation/Variable |
-object to the front of the <dfn>scope chain</dfn> held in the function |
-object's <code>[[scope]]</code> property, so it is important to |
-understand how the internal <code>[[scope]]</code> property is |
-defined. |
-</p> |
- |
-<p> |
-In ECMAScript functions are objects, they are created during variable |
-instantiation from function declarations, during the evaluation of |
-function expressions or by invoking the <code>Function</code> |
-constructor. |
-</p> |
- |
-<p> |
-Function objects created with the <code>Function</code> constructor |
-always have a <code>[[scope]]</code> property referring to a <dfn>scope |
-chain</dfn> that only contains the global object. |
-</p> |
- |
-<p> |
-Function objects created with function declarations or function |
-expressions have the <dfn>scope chain</dfn> of the execution context in which |
-they are created assigned to their internal <code>[[scope]]</code> |
-property. |
-</p> |
- |
-<p> |
-In the simplest case of a global function declaration such as:- |
-</p> |
- |
-<pre> |
-function exampleFunction(formalParameter){ |
- ... <span class="commentJS">// function body code</span> |
-} |
-</pre> |
- |
-<p> |
-- the corresponding function object is created during the variable |
-instantiation for the global execution context. The global execution |
-context has a <dfn>scope chain</dfn> consisting of only the global object. Thus |
-the function object that is created and referred to by the property of |
-the global object with the name "exampleFunction" is |
-assigned an internal <code>[[scope]]</code> property referring to a |
-<dfn>scope chain</dfn> containing only the global object. |
-</p> |
- |
-<p> |
-A similar <dfn>scope chain</dfn> is assigned when a function expression is |
-executed in the global context:- |
-</p> |
- |
-<pre> |
-var exampleFuncRef = function(){ |
- ... <span class="commentJS">// function body code</span> |
-} |
-</pre> |
- |
-<p> |
-- except in this case a named property of the global object is created |
-during variable instantiation for the global execution context but the |
-function object is not created, and a reference to it assigned to the |
-named property of the global object, until the assignment expression is |
-evaluated. But the creation of the function object still happens in the |
-global execution context so the <code>[[scope]]</code> property of the |
-created function object still only contains the global object in the |
-assigned scope chain. |
-</p> |
- |
-<p> |
-Inner function declarations and expressions result in function objects |
-being created within the execution context of a function so they get |
-more elaborate scope chains. Consider the following code, which defines |
-a function with an inner function declaration and then executes the |
-outer function:- |
-</p> |
- |
-<pre> |
-function exampleOuterFunction(formalParameter){ |
- function exampleInnerFuncitonDec(){ |
- ... <span class="commentJS">// inner function body</span> |
- } |
- ... <span class="commentJS">// the rest of the outer function body.</span> |
-} |
- |
-exampleOuterFunction( 5 ); |
-</pre> |
- |
-<p> |
-The function object corresponding with the outer function declaration |
-is created during variable instantiation in the global execution context |
-so its <code>[[scope]]</code> property contains the one item scope |
-chain with only the global object in it. |
-</p> |
- |
-<p> |
-When the global code executes the call to the |
-<code>exampleOuterFunction</code> a new execution context is created for |
-that function call and an Activation/Variable object along with it. |
-The <dfn>scope</dfn> of that new execution context becomes the chain consisting of |
-the new Activation object followed by the chain refereed to by the |
-outer function object's <code>[[scope]]</code> property (just the |
-global object). Variable instantiation for that new execution context |
-results in the creation of a function object that corresponds with the |
-inner function definition and the <code>[[scope]]</code> property of |
-that function object is assigned the value of the <dfn>scope</dfn> from the |
-execution context in which it was created. A <dfn>scope chain</dfn> that contains |
-the Activation object followed by the global object. |
-</p> |
- |
-<p> |
-So far this is all automatic and controlled by the structure and |
-execution of the source code. The <dfn>scope chain</dfn> of the execution context |
-defines the <code>[[scope]]</code> properties of the function objects |
-created and the <code>[[scope]]</code> properties of the function |
-objects define the <dfn>scope</dfn> for their execution contexts (along with the |
-corresponding Activation object). But ECMAScript provides the |
-<code>with</code> statement as a means of modifying the scope chain. |
-</p> |
- |
-<p> |
-The <code>with</code> statement evaluates an expression and if that |
-expression is an object it is added to the <dfn>scope chain</dfn> of the current |
-execution context (in front of the Activation/Variable object). The |
-<code>with</code> statement then executes another statement (that may |
-itself be a block statement) and then restores the execution context's |
-<dfn>scope chain</dfn>to what it was before. |
-</p> |
- |
-<p> |
-A function declaration could not be affected by a <code>with</code> |
-statement as they result in the creation of function objects during |
-variable instantiation, but a function expression can be evaluated |
-inside a <code>with</code> statement:- |
-</p> |
- |
-<pre> |
-<span class="commentJS">/* create a global variable - y - that refers to an object:- */</span> |
-var y = {x:5}; <span class="commentJS">// object literal with an - x - property</span> |
-function exampleFuncWith(){ |
- var z; |
- <span class="commentJS">/* Add the object referred to by the global variable - y - to the |
- front of he scope chain:- |
- */</span> |
- with(y){ |
- <span class="commentJS">/* evaluate a function expression to create a function object |
- and assign a reference to that function object to the local |
- variable - z - :- |
- */</span> |
- z = function(){ |
- ... <span class="commentJS">// inner function expression body;</span> |
- } |
- } |
- ... |
-} |
- |
-<span class="commentJS">/* execute the - exampleFuncWith - function:- */</span> |
-exampleFuncWith(); |
-</pre> |
- |
-<p> |
-When the <code>exampleFuncWith</code> function is called the resulting |
-execution context has a <dfn>scope chain</dfn> consisting of its Activation object |
-followed by the global object. The execution of the <code>with</code> |
-statement adds the object referred to by the global variable |
-<code>y</code> to the front of that <dfn>scope chain</dfn> during the evaluation |
-of the function expression. The function object created by the |
-evaluation of the function expression is assigned a |
-<code>[[scope]]</code> property that corresponds with the <dfn>scope</dfn> of the |
-execution context in which it is created. A <dfn>scope chain</dfn> consisting of |
-object <code>y</code> followed by the Activation object from the |
-execution context of the outer function call, followed by the global |
-object. |
-</p> |
- |
-<p> |
-When the block statement associated with the <code>with</code> |
-statement terminates the <dfn>scope</dfn> of the execution context is restored |
-(the <code>y</code> object is removed), but the function object has |
-been created at that point and its <code>[[scope]]</code> property |
-assigned a reference to a <dfn>scope chain</dfn> with the <code>y</code> object |
-at its head. |
-</p> |
- |
-<h3><a name="clIdRes" id="clIdRes">Identifier Resolution</a></h3> |
- |
-<p> |
-Identifiers are resolved against the scope chain. ECMA 262 categorises |
-<code>this</code> as a keyword rather than an identifier, which is not |
-unreasonable as it is always resolved dependent on the |
-<code>this</code> value in the execution context in which it is used, |
-without reference to the scope chain. |
-</p> |
- |
-<p> |
-Identifier resolution starts with the first object in the scope chain. |
-It is checked to see if it has a property with a name that corresponds |
-with the identifier. Because the <dfn>scope chain</dfn> is a chain of objects |
-this checking encompasses the prototype chain of that object (if it |
-has one). If no corresponding value can be found on the first object |
-in the <dfn>scope chain</dfn> the search progresses to the next object. And so on until |
-one of the objects in the chain (or one of its prototypes) has a |
-property with a name that corresponds with the identifier or the scope |
-chain is exhausted. |
-</p> |
- |
-<p> |
-The operation on the identifier happens in the same way as the use of |
-property accessors on objects described above. The object identified |
-in the <dfn>scope chain</dfn> as having the corresponding property takes the |
-place of the object in the property accessor and the identifier acts |
-as a property name for that object. The global object is always at the |
-end of the scope chain. |
-</p> |
- |
-<p> |
-As execution contexts associated with function calls will have the |
-Activation/Variable object at the front of the chain, identifiers used |
-in function bodies are effectively first checked to see whether they |
-correspond with formal parameters, inner function declaration names or |
-local variables. Those would be resolved as named properties of the |
-Activation/Variable object. |
-</p> |
- |
-<h2><a name="clClose" id="clClose">Closures</a></h2> |
- |
-<h3><a name="clAtGb" id="clAtGb">Automatic Garbage Collection</a></h3> |
- |
-<p> |
-ECMAScript uses automatic garbage collection. The specification |
-does not define the details, leaving that to the implementers to sort |
-out, and some implementations are known to give a very low priority to |
-their garbage collection operations. But the general idea is that if an |
-object becomes un-referable (by having no remaining references to it |
-left accessible to executing code) it becomes available for garbage |
-collection and will at some future point be destroyed and any resources |
-it is consuming freed and returned to the system for re-use. |
-</p> |
- |
-<p> |
-This would normally be the case upon exiting an execution context. The |
-<dfn>scope chain</dfn> structure, the Activation/Variable object and any objects |
-created within the execution context, including function objects, would |
-no longer be accessible and so would become available for garbage |
-collection. |
-</p> |
- |
-<h3><a name="clFrmC" id="clFrmC">Forming Closures</a></h3> |
- |
-<p> |
-A closure is formed by returning a function object that was created |
-within an execution context of a function call from that function call |
-and assigning a reference to that inner function to a property of another |
-object. Or by directly assigning a reference to such a function object |
-to, for example, a global variable, a property of a globally accessible |
-object or an object passed by reference as an argument to the outer |
-function call. e.g:- |
-</p> |
- |
-<pre> |
-function exampleClosureForm(arg1, arg2){ |
- var localVar = 8; |
- function exampleReturned(innerArg){ |
- return ((arg1 + arg2)/(innerArg + localVar)); |
- } |
- <span class="commentJS">/* return a reference to the inner function defined as - |
- exampleReturned -:- |
- */</span> |
- return exampleReturned; |
-} |
- |
-var globalVar = exampleClosureForm(2, 4); |
-</pre> |
- |
-<p> |
-Now the function object created within the execution context of the |
-call to <code>exampleClosureForm</code> cannot be garbage collected |
-because it is referred to by a global variable and is still accessible, |
-it can even be executed with <code>globalVar(n)</code>. |
-</p> |
- |
-<p> |
-But something a little more complicated has happened because the |
-function object now referred to by <code>globalVar</code> was created |
-with a <code>[[scope]]</code> property referring to a scope chain |
-containing the Activation/Variable object belonging to the execution |
-context in which it was created (and the global object). Now the |
-Activation/Variable object cannot be garbage collected either as the |
-execution of the function object referred to by <code>globalVar</code> |
-will need to add the whole <dfn>scope chain</dfn> from its <code>[[scope]]</code> |
-property to the <dfn>scope</dfn> of the execution context created for each call to |
-it. |
-</p> |
- |
-<p> |
-A closure is formed. The inner function object has the free variables |
-and the Activation/Variable object on the function's <dfn>scope chain</dfn> is |
-the environment that binds them. |
-</p> |
- |
-<p> |
-The Activation/Variable object is trapped by being |
-referred to in the <dfn>scope chain</dfn> assigned to the internal |
-<code>[[scope]]</code> property of the function object now referred to |
-by the <code>globalVar</code> variable. The Activation/Variable object |
-is preserved along with its state; the values of its properties. Scope |
-resolution in the execution context of calls to the inner function will |
-resolve identifiers that correspond with named properties of that |
-Activation/Variable object as properties of that object. The value of |
-those properties can still be read and set even though the execution |
-context for which it was created has exited. |
-</p> |
- |
-<p> |
-In the example above that Activation/Variable object has a state that |
-represents the values of formal parameters, inner function definitions |
-and local variables, at the time when the outer function returned |
-(exited its execution context). The <code>arg1</code> property has the |
-value <code>2</code>,the <code>arg2</code> property the value |
-<code>4</code>, <code>localVar</code> the value <code>8</code> and an |
-<code>exampleReturned</code> property that is a reference to the inner |
-function object that was returned form the outer function. (We will be |
-referring to this Activation/Variable object as "ActOuter1" in later |
-discussion, for convenience.) |
-</p> |
- |
-<p> |
-If the <code>exampleClosureForm</code> function was called again as:- |
-</p> |
- |
-<pre> |
-var secondGlobalVar = exampleClosureForm(12, 3); |
-</pre> |
- |
-<p> |
-- a new execution context would be created, along with a new Activation |
-object. And a new function object would be returned, with its own |
-distinct <code>[[scope]]</code> property referring to a scope chain |
-containing the Activation object form this second execution context, |
-with <code>arg1</code> being <code>12</code> and <code>arg2</code> |
-being <code>3</code>. (We will be referring to this Activation/Variable |
-object as "ActOuter2" in later discussion, for convenience.) |
-</p> |
- |
-<p> |
-A second and distinct closure has been formed by the second execution |
-of <code>exampleClosureForm</code>. |
-</p> |
- |
-<p> |
-The two function objects created by the execution of |
-<code>exampleClosureForm</code> to which references have been assigned |
-to the global variable <code>globalVar</code> and |
-<code>secondGlobalVar</code> respectively, return the expression |
-<code>((arg1 + arg2)/(innerArg + localVar))</code>. Which applies |
-various operators to four identifiers. How these identifiers are |
-resolved is critical to the use and value of closures. |
-</p> |
- |
-<p> |
-Consider the execution of the function object referred to by |
-<code>globalVar</code>, as <code>globalVar(2)</code>. A new execution |
-context is created and an Activation object (we will call it |
-"ActInner1"), which is added to the head of the scope chain |
-referred to the <code>[[scope]]</code> property of the executed |
-function object. ActInner1 is given a property named |
-<code>innerArg</code>, after its formal parameter and the argument |
-value <code>2</code> assigned to it. The <dfn>scope chain</dfn> for this new |
-execution context is: <span class="scopeCh">ActInner1-></span> |
-<span class="scopeCh">ActOuter1-></span> |
-<span class="scopeCh">global object</span>. |
-</p> |
- |
-<p> |
-Identifier resolution is done against the <dfn>scope chain</dfn> so in order |
-to return the value of the expression |
-<code>((arg1 + arg2)/(innerArg + localVar))</code> the values of the |
-identifiers will be determined by looking for properties, with names |
-corresponding with the identifiers, on each object in the scope chain |
-in turn. |
-</p> |
- |
-<p> |
-The first object in the chain is ActInner1 and it has a property named |
-<code>innerArg</code> with the value <code>2</code>. All of the other |
-3 identifiers correspond with named properties of ActOuter1; |
-<code>arg1</code> is <code>2</code>, <code>arg2</code> is |
-<code>4</code> and <code>localVar</code> is <code>8</code>. The |
-function call returns <code>((2 + 4)/(2 + 8))</code>. |
-</p> |
- |
-<p> |
-Compare that with the execution of the otherwise identical function |
-object referred to by <code>secondGlobalVar</code>, as |
-<code>secondGlobalVar(5)</code>. Calling the Activation object for |
-this new execution context "ActInner2", the scope chain |
-becomes: <span class="scopeCh">ActInner2-></span> |
-<span class="scopeCh">ActOuter2-></span> |
-<span class="scopeCh">global object</span>. ActInner2 returns |
-<code>innerArg</code> as <code>5</code> and ActOuter2 returns |
-<code>arg1</code>, <code>arg2</code> and <code>localVar</code> as |
-<code>12</code>, <code>3</code> and <code>8</code> respectively. The |
-value returned is <code>((12 + 3)/(5 + 8))</code>. |
-</p> |
- |
-<p> |
-Execute <code>secondGlobalVar</code> again and a new Activation object |
-will appear at the front of the <dfn>scope chain</dfn> but ActOuter2 will still |
-be next object in the chain and the value of its named properties will |
-again be used in the resolution of the identifiers <code>arg1</code>, |
-<code>arg2</code> and <code>localVar</code>. |
-</p> |
- |
-<p> |
-This is how ECMAScript inner functions gain, and maintain, access to the formal |
-parameters, declared inner functions and local variables of the |
-execution context in which they were created. And it is how the |
-forming of a closure allows such a function object to keep referring |
-to those values, reading and writing to them, for as long as it |
-continues to exist. The Activation/Variable object from the execution |
-context in which the inner function was created remains on the scope |
-chain referred to by the function object's <code>[[scope]]</code> |
-property, until all references to the inner function are freed and |
-the function object is made available for garbage collection (along |
-with any now unneeded objects on its scope chain). |
-</p> |
- |
-<p> |
-Inner function may themselves have inner functions, and the inner |
-functions returned from the execution of functions to form closures |
-may themselves return inner functions and form closures of their own. |
-With each nesting the <dfn>scope chain</dfn> gains extra Activation objects |
-originating with the execution contexts in which the inner function |
-objects were created. The ECMAScript specification requires a scope |
-chain to be finite, but imposes no limits on their length. |
-Implementations probably do impose some practical limitation but no |
-specific magnitude has yet been reported. The potential for nesting |
-inner functions seems so far to have exceeded anyone's desire to |
-code them. |
-</p> |
- |
-<h2><a name="clClDo" id="clClDo">What can be done with Closures?</a></h2> |
- |
-<p> |
-Strangely the answer to that appears to be anything and everything. |
-I am told that closures enable ECMAScript to emulate anything, so the |
-limitation is the ability to conceive and implement the emulation. That |
-is a bit esoteric and it is probably better to start with something a |
-little more practical. |
-</p> |
- |
- |
-<h3><a name="clSto" id="clSto">Example 1: setTimeout with Function References</a></h3> |
- |
- |
-<p> |
-A common use for a closure is to provide parameters for the execution |
-of a function prior to the execution of that function. For example, |
-when a function is to be provided as the first argument to the |
-<code>setTimout</code> function that is common in web browser |
-environments. |
-</p> |
- |
-<p> |
-<code>setTimeout</code> schedules the execution of a function (or a |
-string of javascript source code, but not in this context), provided as |
-its first argument, after an interval expressed in milliseconds (as its |
-second argument). If a piece of code wants to use |
-<code>setTimeout</code> it calls the <code>setTimeout</code> function |
-and passes a reference to a function object as the first argument and |
-the millisecond interval as the second, but a reference to a function |
-object cannot provide parameters for the scheduled execution of that |
-function. |
-</p> |
- |
-<p> |
-However, code could call another function that returned a reference to |
-an inner function object, with that inner function object being passed |
-by reference to the <code>setTimeout</code> function. The parameters to |
-be used for the execution of the inner function are passed with the |
-call to the function that returns it. <code>setTimout</code> executes |
-the inner function without passing arguments but that inner function |
-can still access the parameters provided by the call to the outer |
-function that returned it:- |
-</p> |
- |
-<pre> |
-function callLater(paramA, paramB, paramC){ |
- <span class="commentJS">/* Return a reference to an anonymous inner function created |
- with a function expression:- |
- */</span> |
- return (function(){ |
- <span class="commentJS">/* This inner function is to be executed with - setTimeout |
- - and when it is executed it can read, and act upon, the |
- parameters passed to the outer function:- |
- */</span> |
- paramA[paramB] = paramC; |
- }); |
-} |
- |
-... |
- |
-<span class="commentJS">/* Call the function that will return a reference to the inner function |
- object created in its execution context. Passing the parameters that |
- the inner function will use when it is eventually executed as |
- arguments to the outer function. The returned reference to the inner |
- function object is assigned to a local variable:- |
-*/</span> |
-var functRef = callLater(elStyle, "display", "none"); |
-<span class="commentJS">/* Call the setTimeout function, passing the reference to the inner |
- function assigned to the - functRef - variable as the first argument:- |
-*/</span> |
-hideMenu=setTimeout(functRef, 500); |
-</pre> |
- |
-<h3><a name="clObjI" id="clObjI">Example 2: Associating Functions with Object Instance Methods</a></h3> |
- |
-<p> |
-There are many other circumstances when a reference to a function |
-object is assigned so that it would be executed at some future time |
-where it is useful to provide parameters for the execution of that |
-function that would not be easily available at the time of execution |
-but cannot be known until the moment of assignment. |
-</p> |
- |
-<p> |
-One example might be a javascript object that is designed to |
-encapsulate the interactions with a particular DOM element. It has |
-<code>doOnClick</code>, <code>doMouseOver</code> and |
-<code>doMouseOut</code> methods and wants to execute those methods |
-when the corresponding events are triggered on the DOM element, but |
-there may be any number of instances of the javascript object created |
-associated with different DOM elements and the individual object |
-instances do not know how they will be employed by the code that |
-instantiated them. The object instances do not know how to reference |
-themselves globally because they do not know which global variables |
-(if any) will be assigned references to their instances. |
-</p> |
- |
-<p> |
-So the problem is to execute an event handling function that has an |
-association with a particular instance of the javascript object, and |
-knows which method of that object to call. |
-</p> |
- |
-<p> |
-The following example uses a small generalised closure based function |
-that associates object instances with element event handlers. |
-Arranging that the execution of the event handler calls the specified |
-method of the object instance, passing the event object and a reference |
-to the associated element on to the object method and returning the |
-method's return value. |
-</p> |
- |
-<pre> |
-<span class="commentJS">/* A general function that associates an object instance with an event |
- handler. The returned inner function is used as the event handler. |
- The object instance is passed as the - obj - parameter and the name |
- of the method that is to be called on that object is passed as the - |
- methodName - (string) parameter. |
-*/</span> |
-function associateObjWithEvent(obj, methodName){ |
- <span class="commentJS">/* The returned inner function is intended to act as an event |
- handler for a DOM element:- |
- */</span> |
- return (function(e){ |
- <span class="commentJS">/* The event object that will have been parsed as the - e - |
- parameter on DOM standard browsers is normalised to the IE |
- event object if it has not been passed as an argument to the |
- event handling inner function:- |
- */</span> |
- e = e||window.event; |
- <span class="commentJS">/* The event handler calls a method of the object - obj - with |
- the name held in the string - methodName - passing the now |
- normalised event object and a reference to the element to |
- which the event handler has been assigned using the - this - |
- (which works because the inner function is executed as a |
- method of that element because it has been assigned as an |
- event handler):- |
- */</span> |
- return obj[methodName](e, this); |
- }); |
-} |
- |
-<span class="commentJS">/* This constructor function creates objects that associates themselves |
- with DOM elements whose IDs are passed to the constructor as a |
- string. The object instances want to arrange than when the |
- corresponding element triggers onclick, onmouseover and onmouseout |
- events corresponding methods are called on their object instance. |
-*/</span> |
-function DhtmlObject(elementId){ |
- <span class="commentJS">/* A function is called that retrieves a reference to the DOM |
- element (or null if it cannot be found) with the ID of the |
- required element passed as its argument. The returned value |
- is assigned to the local variable - el -:- |
- */</span> |
- var el = getElementWithId(elementId); |
- <span class="commentJS">/* The value of - el - is internally type-converted to boolean for |
- the - if - statement so that if it refers to an object the |
- result will be true, and if it is null the result false. So that |
- the following block is only executed if the - el - variable |
- refers to a DOM element:- |
- */</span> |
- if(el){ |
- <span class="commentJS">/* To assign a function as the element's event handler this |
- object calls the - associateObjWithEvent - function |
- specifying itself (with the - this - keyword) as the object |
- on which a method is to be called and providing the name of |
- the method that is to be called. The - associateObjWithEvent |
- - function will return a reference to an inner function that |
- is assigned to the event handler of the DOM element. That |
- inner function will call the required method on the |
- javascript object when it is executed in response to |
- events:- |
- */</span> |
- el.onclick = associateObjWithEvent(this, "doOnClick"); |
- el.onmouseover = associateObjWithEvent(this, "doMouseOver"); |
- el.onmouseout = associateObjWithEvent(this, "doMouseOut"); |
- ... |
- } |
-} |
-DhtmlObject.prototype.doOnClick = function(event, element){ |
- ... <span class="commentJS">// doOnClick method body</span>. |
-} |
-DhtmlObject.prototype.doMouseOver = function(event, element){ |
- ... <span class="commentJS">// doMouseOver method body.</span> |
-} |
-DhtmlObject.prototype.doMouseOut = function(event, element){ |
- ... <span class="commentJS">// doMouseOut method body.</span> |
-} |
-</pre> |
- |
-<p> |
-And so any instances of the <code>DhtmlObject</code> can associate themselves |
-with the DOM element that they are interested in without any need |
-to know anything about how they are being employed by other code, |
-impacting on the global namespace or risking clashes with other |
-instances of the <code>DhtmlObject</code>. |
-</p> |
- |
-<h3><a name="clEncap" id="clEncap">Example 3: Encapsulating Related Functionality</a></h3> |
- |
-<p> |
-Closures can be used to create additional scopes that can be used to |
-group interrelated and dependent code in a way that minimises the risk |
-of accidental interaction. Suppose a function is to build a string and |
-to avoid the repeated concatenation operations (and the creation of |
-numerous intermediate strings) the desire is to use an array to store |
-the parts of the string in sequence and then output the results using |
-the <code>Array.prototype.join</code> method (with an empty string as its argument). |
-The array is going to act as a buffer for the output, but defining it |
-locally to the function will result in its re-creation on each |
-execution of the function, which may not be necessary if the only |
-variable content of that array will be re-assigned on each function |
-call. |
-</p> |
- |
-<p> |
-One approach might make the array a global variable so that it can be |
-re-used without being re-created. But the consequences of that will be |
-that, in addition to the global variable that refers to the function |
-that will use the buffer array, there will be a second global property |
-that refers to the array itself. The effect is to render the code less |
-manageable, as, if it is to be used elsewhere, its author has to remember |
-to include both the function definition and the array definition. It |
-also makes the code less easy to integrate with other code because |
-instead of just ensuring that the function name is unique within the |
-global namespace it is necessary to ensure that the Array on which it |
-is dependent is using a name that is unique within the global |
-namespace. |
-</p> |
- |
-<p> |
-A Closure allows the buffer array to be associated (and neatly |
-packaged) with the function that is dependent upon it and |
-simultaneously keep the property name to which the buffer array as |
-assigned out of the global namespace and free of the risk of name |
-conflicts and accidental interactions. |
-</p> |
- |
-<p> |
-The trick here is to create one additional execution context by |
-executing a function expression in-line and have that function |
-expression return an inner function that will be the function that is |
-used by external code. The buffer array is then defined as a local |
-variable of the function expression that is executed in-line. That only |
-happens once so the Array is only created once, but is available to |
-the function that depends on it for repeated use. |
-</p> |
- |
-<p> |
-The following code creates a function that will return a string of |
-<span class="initialism" title="HyperText Mark-up Language"><abbr title="HyperText Mark-up Language">HTML</abbr></span>, much of which is constant, but those constant character |
-sequences need to be interspersed with variable information provided |
-as parameter to the function call. |
-</p> |
- |
-<p> |
-A reference to an inner function object is returned from the in-line |
-execution of a function expression and assigned to a global variable |
-so that it can be called as a global function. The buffer array is |
-defined as a local variable in the outer function expression. It is |
-not exposed in the global namespace and does not need to be re-created |
-whenever the function that uses it is called. |
-</p> |
- |
-<pre> |
-<span class="commentJS">/* A global variable - getImgInPositionedDivHtml - is declared and |
- assigned the value of an inner function expression returned from |
- a one-time call to an outer function expression. |
- |
- That inner function returns a string of <span class="initialism" title="HyperText Mark-up Language"><abbr title="HyperText Mark-up Language">HTML</abbr></span> that represents an |
- absolutely positioned DIV wrapped round an IMG element, such that |
- all of the variable attribute values are provided as parameters |
- to the function call:- |
-*/</span> |
-var getImgInPositionedDivHtml = (function(){ |
- <span class="commentJS">/* The - buffAr - Array is assigned to a local variable of the |
- outer function expression. It is only created once and that one |
- instance of the array is available to the inner function so that |
- it can be used on each execution of that inner function. |
- |
- Empty strings are used as placeholders for the date that is to |
- be inserted into the Array by the inner function:- |
- */</span> |
- var buffAr = [ |
- '<div id="', |
- '', <span class="commentJS">//index 1, DIV ID attribute</span> |
- '" style="position:absolute;top:', |
- '', <span class="commentJS">//index 3, DIV top position</span> |
- 'px;left:', |
- '', <span class="commentJS">//index 5, DIV left position</span> |
- 'px;width:', |
- '', <span class="commentJS">//index 7, DIV width</span> |
- 'px;height:', |
- '', <span class="commentJS">//index 9, DIV height</span> |
- 'px;overflow:hidden;\"><img src=\"', |
- '', <span class="commentJS">//index 11, IMG URL</span> |
- '\" width=\"', |
- '', <span class="commentJS">//index 13, IMG width</span> |
- '\" height=\"', |
- '', <span class="commentJS">//index 15, IMG height</span> |
- '\" alt=\"', |
- '', <span class="commentJS">//index 17, IMG alt text</span> |
- '\"><\/div>' |
- ]; |
- <span class="commentJS">/* Return the inner function object that is the result of the |
- evaluation of a function expression. It is this inner function |
- object that will be executed on each call to - |
- getImgInPositionedDivHtml( ... ) -:- |
- */</span> |
- return (function(url, id, width, height, top, left, altText){ |
- <span class="commentJS">/* Assign the various parameters to the corresponding |
- locations in the buffer array:- |
- */</span> |
- buffAr[1] = id; |
- buffAr[3] = top; |
- buffAr[5] = left; |
- buffAr[13] = (buffAr[7] = width); |
- buffAr[15] = (buffAr[9] = height); |
- buffAr[11] = url; |
- buffAr[17] = altText; |
- <span class="commentJS">/* Return the string created by joining each element in the |
- array using an empty string (which is the same as just |
- joining the elements together):- |
- */</span> |
- return buffAr.join(''); |
- }); <span class="commentJS">//:End of inner function expression.</span> |
-})(); |
-<span class="commentJS">/*^^- :The inline execution of the outer function expression. */</span> |
-</pre> |
- |
-<p> |
-If one function was dependent on one (or several) other functions, but |
-those other functions were not expected to be directly employed by any |
-other code, then the same technique could be used to group those |
-functions with the one that was to be publicly exposed. Making a |
-complex multi-function process into an easily portable and encapsulated |
-unit of code. |
-</p> |
- |
-<h3><a name="clOtE" id="clOtE">Other Examples</a></h3> |
- |
-<p> |
-Probably one of the best known applications of closures is |
-<a href="http://www.crockford.com/javascript/private.html">Douglas |
-Crockford's technique for the emulation of private instance variables |
-in ECMAScript objects</a>. Which can be extended to all sorts of |
-structures of <dfn>scope</dfn> contained nested accessibility/visibility, including |
-<a href="http://myweb.tiscali.co.uk/cornford/js_info/private_static.html"> |
-the emulation of private static members for ECMAScript objects</a>. |
-</p> |
- |
-<p> |
-The possible application of closures are endless, understanding how |
-they work is probably the best guide to realising how they can be |
-used. |
-</p> |
- |
-<h2 id="clAc">Accidental Closures</h2> |
- |
-<p> |
-Rendering any inner function accessible outside of the body of the |
-function in which it was created will form a closure. That makes |
-closures very easy to create and one of the consequences is that |
-javascript authors who do not appreciate closures as a language feature |
-can observe the use of inner functions for various tasks and employ |
-inner functions, with no apparent consequences, not realising that |
-closures are being created or what the implications of doing that are. |
-</p> |
- |
-<p> |
-Accidentally creating closures can have harmful side effects as the |
-following section on the IE memory leak problem describes, but they can |
-also impact of the efficiency of code. It is not the closures |
-themselves, indeed carefully used they can contribute significantly |
-towards the creation of efficient code. It is the use of inner |
-functions that can impact on efficiency. |
-</p> |
- |
-<p> |
-A common situation is where inner functions are used is as event |
-handlers for DOM elements. For example the following code might be used |
-to add an onclick handler to a link element:- |
-</p> |
- |
-<pre> |
-<span class="commentJS">/* Define the global variable that is to have its value added to the |
- - href - of a link as a query string by the following function:- |
-*/</span> |
-var quantaty = 5; |
-<span class="commentJS">/* When a link passed to this function (as the argument to the function |
- call - linkRef -) an onclick event handler is added to the link that |
- will add the value of a global variable - quantaty - to the - href - |
- of that link as a query string, then return true so that the link |
- will navigate to the resource specified by the - href - which will |
- by then include the assigned query string:- |
-*/</span> |
-function addGlobalQueryOnClick(linkRef){ |
- <span class="commentJS">/* If the - linkRef - parameter can be type converted to true |
- (which it will if it refers to an object):- |
- */</span> |
- if(linkRef){ |
- <span class="commentJS">/* Evaluate a function expression and assign a reference to the |
- function object that is created by the evaluation of the |
- function expression to the onclick handler of the link |
- element:- |
- */</span> |
- linkRef.onclick = function(){ |
- <span class="commentJS">/* This inner function expression adds the query string to |
- the - href - of the element to which it is attached as |
- an event handler:- |
- */</span> |
- this.href += ('?quantaty='+escape(quantaty)); |
- return true; |
- }; |
- } |
-} |
-</pre> |
- |
-<p> |
-Whenever the <code>addGlobalQueryOnClick</code> function is called a |
-new inner function is created (and a closure formed by its assignment). |
-From the efficiency point of view that would not be significant if the |
-<code>addGlobalQueryOnClick</code> function was only called once or |
-twice, but if the function was heavily employed many distinct function |
-objects would be created (one for each evaluation of the inner function |
-expression). |
-</p> |
- |
-<p> |
-The above code is not taking advantage of the fact that inner functions |
-are becoming accessible outside of the function in which they are being |
-created (or the resulting closures). As a result exactly the same effect |
-could be achieved by defining the function that is to be used as the |
-event handler separately and then assigning a reference to that |
-function to the event handling property. Only one function object would |
-be created and all of the elements that use that event handler would |
-share a reference to that one function:- |
-</p> |
- |
-<pre> |
-<span class="commentJS">/* Define the global variable that is to have its value added to the |
- - href - of a link as a query string by the following function:- |
-*/</span> |
-var quantaty = 5; |
- |
-<span class="commentJS">/* When a link passed to this function (as the argument to the function |
- call - linkRef -) an onclick event handler is added to the link that |
- will add the value of a global variable - quantaty - to the - href - |
- of that link as a query string, then return true so that the link |
- will navigate to the resource specified by the - href - which will |
- by then include the assigned query string:- |
-*/</span> |
-function addGlobalQueryOnClick(linkRef){ |
- <span class="commentJS">/* If the - linkRef - parameter can be type converted to true |
- (which it will if it refers to an object):- |
- */</span> |
- if(linkRef){ |
- <span class="commentJS">/* Assign a reference to a global function to the event |
- handling property of the link so that it becomes the |
- element's event handler:- |
- */</span> |
- linkRef.onclick = forAddQueryOnClick; |
- } |
-} |
-<span class="commentJS">/* A global function declaration for a function that is intended to act |
- as an event handler for a link element, adding the value of a global |
- variable to the - href - of an element as an event handler:- |
-*/</span> |
-function forAddQueryOnClick(){ |
- this.href += ('?quantaty='+escape(quantaty)); |
- return true; |
-} |
-</pre> |
- |
-<p> |
-As the inner function in the first version is not being used to exploit |
-the closures produced by its use, it would be more efficient not to use |
-an inner function, and thus not repeat the process of creating many |
-essentially identical function objects. |
-</p> |
- |
-<p> |
-A similar consideration applies to object constructor functions. It is |
-not uncommon to see code similar to the following skeleton constructor:- |
-</p> |
- |
- |
-<pre> |
-function ExampleConst(param){ |
- <span class="commentJS">/* Create methods of the object by evaluating function expressions |
- and assigning references to the resulting function objects |
- to the properties of the object being created:- |
- */</span> |
- this.method1 = function(){ |
- ... <span class="commentJS">// method body.</span> |
- }; |
- this.method2 = function(){ |
- ... <span class="commentJS">// method body.</span> |
- }; |
- this.method3 = function(){ |
- ... <span class="commentJS">// method body.</span> |
- }; |
- <span class="commentJS">/* Assign the constructor's parameter to a property of the object:- |
- */</span> |
- this.publicProp = param; |
-} |
-</pre> |
- |
-<p> |
-Each time the constructor is used to create an object, with |
-<code>new ExampleConst(n)</code>, a new set of function objects are |
-created to act as its methods. So the more object instances that are |
-created the more function objects are created to go with them. |
-</p> |
- |
-<p> |
-Douglas Crockford's technique for emulating private members on |
-javascript objects exploits the closure resulting form assigning |
-references to inner function objects to the public properties of a |
-constructed object from within its constructor. But if the methods of |
-an object are not taking advantage of the closure that they will form |
-within the constructor the creation of multiple function objects for |
-each object instantiation will make the instantiation process slower |
-and more resources will be consumed to accommodate the extra function |
-objects created. |
-</p> |
- |
-<p> |
-In that case it would be more efficient to create the function object |
-once and assign references to them to the corresponding properties of |
-the constructor's <code>prototype</code> so they may be shared by all |
-of the objects created with that constructor:- |
-</p> |
- |
-<pre> |
-function ExampleConst(param){ |
- <span class="commentJS">/* Assign the constructor's parameter to a property of the object:- |
- */</span> |
- this.publicProp = param; |
-} |
-<span class="commentJS">/* Create methods for the objects by evaluating function expressions |
- and assigning references to the resulting function objects to the |
- properties of the constructor's prototype:- |
-*/</span> |
-ExampleConst.prototype.method1 = function(){ |
- ... <span class="commentJS">// method body.</span> |
-}; |
-ExampleConst.prototype.method2 = function(){ |
- ... <span class="commentJS">// method body.</span> |
-}; |
-ExampleConst.prototype.method3 = function(){ |
- ... <span class="commentJS">// method body.</span> |
-}; |
- |
-</pre> |
-<h2><a name="clMem" id="clMem">The Internet Explorer Memory Leak Problem</a></h2> |
- |
-<p> |
-The Internet Explorer web browser (verified on versions 4 to 6 (6 is |
-current at the time of writing)) has a fault in its garbage collection |
-system that prevents it from garbage collecting ECMAScript and some |
-host objects if those host objects form part of a "circular" |
-reference. The host objects in question are any DOM Nodes (including |
-the document object and its descendants) and ActiveX objects. If a |
-circular reference is formed including one or more of them, then |
-none of the objects involved will be freed until the browser is closed |
-down, and the memory that they consume will be unavailable to the |
-system until that happens. |
-</p> |
- |
-<p> |
-A circular reference is when two or more objects refer to each other in |
-a way that can be followed and lead back to the starting point. Such |
-as object 1 has a property that refers to object 2, object 2 has a |
-property that refers to object 3 and object 3 has a property that |
-refers back to object 1. With pure ECMAScript objects as soon as no |
-other objects refer to any of objects 1, 2 or 3 the fact that they only |
-refer to each other is recognised and they are made available for |
-garbage collection. But on Internet Explorer, if any of those objects |
-happen to be a DOM Node or ActiveX object, the garbage collection |
-cannot see that the circular relationship between them is isolated |
-from the rest of the system and free them. Instead they all stay in |
-memory until the browser is closed. |
-</p> |
- |
-<p> |
-Closures are extremely good at forming circular references. If a |
-function object that forms a closure is assigned as, for example, and |
-event handler on a DOM Node, and a reference to that Node is assigned |
-to one of the Activation/Variable objects in its <dfn>scope chain</dfn> then a |
-circular reference exists. |
-<span class="scopeCh">DOM_Node.onevent -></span> |
-<span class="scopeCh">function_object.[[scope]] -></span> |
-<span class="scopeCh">scope_chain -></span> |
-<span class="scopeCh">Activation_object.nodeRef -></span> |
-<span class="scopeCh">DOM_Node</span>. |
-It is very easy to do, and a bit of browsing around a site that forms |
-such a reference in a piece of code common to each page can consume |
-most of the systems memory (possibly all). |
-</p> |
- |
-<p> |
-Care can be taken to avoid forming circular references and remedial |
-action can be taken when they cannot otherwise be avoided, such as |
-using IE's onunload event to null event handling function |
-references. Recognising the problem and understanding closures |
-(and their mechanism) is the key to avoiding this problem with IE. |
-</p> |
- |
-<p id="rToc"> |
-<a href="faq_notes.html#toc">comp.lang.javascript FAQ notes T.O.C.</a> |
-</p> |
- |
-<ul style="list-style-type:none;margin-top:2.5em;"> |
- <li>Written by <span class="person">Richard Cornford</span>. March 2004.</li> |
- <li>With corrections and suggestions by:-<br> |
- <ul style="list-style-type:none;"> |
- <li><span class="person">Martin Honnen</span>.</li> |
- <li><span class="person">Yann-Erwan Perio (Yep)</span>.</li> |
- <li><span class="person">Lasse Reichstein Nielsen</span>. (<a href="#clDefN">definition of closure</a>)</li> |
- <li><span class="person">Mike Scirocco</span>.</li> |
- <li><span class="person">Dr John Stockton</span>.</li> |
- <li><span class="person">Garrett Smith</span>.</li> |
- </ul> |
- </li> |
-</ul> |
-</body> |
-</html> |
/cljs/faq_notes/closures.html |
Property changes: |
Deleted: svn:mime-type |
## -1 +0,0 ## |
-text/plain |
\ No newline at end of property |
Index: cljs/faq_notes/type_convert.html |
=================================================================== |
--- cljs/faq_notes/type_convert.html (revision 43) |
+++ cljs/faq_notes/type_convert.html (nonexistent) |
@@ -1,1319 +0,0 @@ |
-<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" |
-"http://www.w3.org/TR/html4/strict.dtd"> |
-<html lang="en"> |
-<head> |
- <title>Javascript Type-Conversion</title> |
- <meta HTTP-EQUIV="Content-Type" CONTENT="text/html; charset=iso-8859-1"> |
-<link href="../../faq.css" rel="stylesheet" type="text/css"> |
-<link href="../notes.css" rel="stylesheet" type="text/css"> |
-<style type="text/css"> |
-td { |
- text-align:center; |
-} |
-pre.st { |
- background-color:#EEEEFF; |
- margin:0px; |
- padding:0px; |
- text-align:left; |
- border:0px none #EEEEFF; |
-} |
-caption { |
- font-weight:bold; |
-} |
-th, td { |
- vertical-align:baseline; |
- background-color:#EEEEFF; |
- color:#000000; |
- white-space:nowrap; |
- padding:2px |
-} |
-.true { |
- color:#003444; |
- background-color:#E0FFE0; |
-} |
-.false { |
- color:#003444; |
- background-color:#FFE0E0; |
-} |
-table { |
- margin:1em 2.5em; |
-} |
- </style> |
- </head> |
- <body> |
- |
-<h1>Javascript Type-Conversion</h1> |
-<div id="faqNav"> |
- <a href="/faq/">FAQ</a> > <a href="/faq/notes/">FAQ Notes</a> |
-</div> |
- |
-<ul> |
- <li><a href="#tcInt">Introduction</a></li> |
- <li><a href="#tcBool">Converting to Boolean</a></li> |
- <li><a href="#tcString">Converting to String</a></li> |
- <li><a href="#tcNumber">Converting to Number</a></li> |
- <li><a href="#tcParse">Parsing to Number</a> |
- <ul> |
- <li><a href="#tcParseFl">parseFloat</a></li> |
- <li><a href="#tcParseIn">parseInt</a></li> |
- <li><a href="#tcPrIntRx">parseInt with a radix argument</a></li> |
- </ul> |
- </li> |
- <li><a href="#tcToInt32">ToInt32</a></li> |
- <li><a href="#tcUserIn">Converting User Input</a> |
- <ul> |
- <li><a href="#tcRegEx">Regular expression examples</a></li> |
- </ul> |
- </li> |
-</ul> |
- |
-<h2 id="tcInt">Introduction</h2> |
- |
-<p id="tcInt_1"> |
-Javascript (ECMAScript) is a loosely typed language. That does not mean |
-that it has no data types just that the value of a variable or a Javascript |
-object property does not need to have a particular type of value assigned |
-to it, or that it should always hold the same type of value. Javascript |
-also freely type-converts values into a type suitable for (or required by) |
-the context of their use. |
-</p> |
- |
-<p id="tcInt_2"> |
-Javascript being loosely typed and willing to type-convert still does not |
-save the programmer from needing to think about the actual type of values |
-that they are dealing with. A very common error in browser scripting, for |
-example, is to read the value property of a form control into which the |
-user is expected to type a number and then add that value to another |
-number. Because the value properties of form controls are strings (even if |
-the character sequence they contain represents a number) the attempt to |
-add that string to a value, even if that value happens to be a number, |
-results in the second value being type-converted into a string and |
-concatenated to the end of the first string value from the from control. |
-</p> |
- |
-<p id="tcInt_3"> |
-That problem arises from the dual nature of the <code>+</code> operator |
-used for both numeric addition and string concatenation. With which the |
-nature of the operation performed is determined by the context, where |
-only if both operands are numbers to start with will the <code>+</code> |
-operator perform addition. Otherwise it converts all of its operands to |
-strings and does concatenation. |
-</p> |
- |
-<p id="tcInt_4"> |
-The following discussion is illustrated with Javascript generated tables |
-of values resulting from the conversion operations. The headers of those |
-tables display the values as represented in the Javascript source code |
-used rather than their internal representation. So, for example |
-<code>123e-2</code> as a number was the character sequence typed into |
-the source code, the interpreter reads that and generates the |
-number value 1.23 from it for internal use. The various values used for |
-the tests have been chosen to illustrate aspects of type |
-converting, those aspects may not apply to all of the tables presented. |
-However, all of the test values are included in all of the tables (except |
-where no type converting occurs) for full comparison. The bodies of the |
-tables list the results of the various type conversion operations. |
-</p> |
- |
-<p id="tcInt_5"> |
-If you are accepting/using this page's <span class="initialism" title="Cascading Style Sheet"><abbr title="Cascading Style Sheet">CSS</abbr></span> style suggestions the type |
-of the values at various stages is illustrated by the colour of the text |
-used. The following key shows those type/colour relationships, they are |
-derived from the string values returned by the <code>typeof</code> |
-operator (which returns <code>"object"</code> |
-for the <code>null</code> type when in reality <code>null</code> is |
-distinct from objects). |
-</p> |
- |
-<table id="tcInt_key"> |
- <tbody> |
- <tr><th>Key</th></tr> |
- <tr><td class="st">string</td></tr> |
- <tr><td class="nm">number</td></tr> |
- <tr><td class="bl">boolean</td></tr> |
- <tr><td class="ob">object</td></tr> |
- <tr><td class="fn" style="text-align:center;">function</td></tr> |
- <tr><td class="ob">null</td></tr> |
- <tr><td class="un">undefined</td></tr> |
- </tbody> |
-</table> |
- |
-<p id="tcInt_6"> |
-The boolean values of results also have a coloured background to highlight |
- <code>true</code> or <code>false</code>. |
-</p> |
- |
-<h2 id="tcBool">Converting to Boolean</h2> |
- |
-<p id="tcBool_1"> |
-When evaluating the expression of an <code>if</code> statement the Javascript |
-interpreter will type-convert the result of that expression to boolean |
-in order to make its decision. Also various operators internally |
-type-convert their operands to boolean in order to determine what |
-action to take. These include the logical operators like AND |
-(<code>&&</code>), OR (<code>||</code>) and NOT (<code>!</code>). The NOT |
-operator type-converts its operand to boolean and if that value is |
-boolean true it returns false and if false it returns true. As the |
-result of a NOT operation is a boolean value that is the inverse of |
-the type-converted true-ness of its operand, two NOT operations |
-together will return a boolean value that is equivalent to the result |
-of type-converting the operand to boolean:- |
-</p> |
- |
-<pre id="tcBool_ex1"> |
-var boolValue = !!x; |
-</pre> |
- |
-<p id="tcBool_2"> |
-That technique has been used to generate the following tables. |
-</p> |
- |
-<p id="tcBool_3"> |
-An alternative method of generating a boolean value that represents |
-the type-converted true-ness of a value is to pass that value to |
-the <code>Boolean</code> constructor called as a function:- |
-</p> |
- |
-<pre id="tcBool_ex2"> |
-var boolValue = Boolean(x); |
-</pre> |
- |
-<table> |
- <caption>Double NOT (!!col) : Numeric Values.</caption> |
- <thead> |
- <tr> |
- <th></th> |
- <th class="nm">-1.6</th> |
- <th class="nm">-0</th> |
- <th class="nm">+0</th> |
- <th class="nm">1</th> |
- <th class="nm">1.6</th> |
- <th class="nm">8</th> |
- <th class="nm">16</th> |
- <th class="nm">16.8</th> |
- <th class="nm">123e-2</th> |
- <th class="nm">-Infinity</th> |
- <th class="nm">+Infinity</th> |
- <th class="nm">NaN</th> |
- </tr> |
- </thead> |
- <tbody> |
- <tr> |
- <th>!!col</th> |
- <td class="true">true</td> |
- <td class="false">false</td> |
- <td class="false">false</td> |
- <td class="true">true</td> |
- <td class="true">true</td> |
- <td class="true">true</td> |
- <td class="true">true</td> |
- <td class="true">true</td> |
- <td class="true">true</td> |
- <td class="true">true</td> |
- <td class="true">true</td> |
- <td class="false">false</td> |
- </tr> |
- </tbody> |
-</table> |
- |
-<p id="tcBool_4"> |
-When numbers are converted to boolean, zero becomes false and all other |
-numbers are true. With the excepting of the special numeric value |
-<code>NaN</code> (Not a Number) which is used when another type is |
-converted to a number but that conversion does not result in a |
-meaningful number. <code>NaN</code> is always false. The values of |
-positive and negative infinity, while not finite numbers, are non-zero |
-numeric values and always type-convert to boolean <code>true</code>. |
-</p> |
- |
-<table> |
- <caption>Double NOT (!!col) : String Values.</caption> |
- <thead> |
- <tr> |
- <th></th> |
- <th class="st">""<br>(empty<br>string)</th> |
- <th class="st">"-1.6"</th> |
- <th class="st">"0"</th> |
- <th class="st">"1"</th> |
- <th class="st">"1.6"</th> |
- <th class="st">"8"</th> |
- <th class="st">"16"</th> |
- <th class="st">"16.8"</th> |
- <th class="st">"123e-2"</th> |
- <th class="st">"010"<br>(Octal)</th> |
- <th class="st">"0x10"<br>(Hex)</th> |
- <th class="st">"0xFF"<br>(Hex)</th> |
- <th class="st">"-010"</th> |
- <th class="st">"-0x10"</th> |
- <th class="st">"xx"</th> |
- </tr> |
- </thead> |
- <tbody> |
- <tr> |
- <th>!!col</th> |
- <td class="false">false</td> |
- <td class="true">true</td> |
- <td class="true">true</td> |
- <td class="true">true</td> |
- <td class="true">true</td> |
- <td class="true">true</td> |
- <td class="true">true</td> |
- <td class="true">true</td> |
- <td class="true">true</td> |
- <td class="true">true</td> |
- <td class="true">true</td> |
- <td class="true">true</td> |
- <td class="true">true</td> |
- <td class="true">true</td> |
- <td class="true">true</td> |
- </tr> |
- </tbody> |
-</table> |
- |
-<p id="tcBool_5"> |
-Type conversion rules are even simpler for string to boolean conversion |
-as all non-empty strings always become true and empty strings become |
-false. |
-</p> |
- |
-<table> |
- <caption>Double NOT (!!col) : Other Values</caption> |
- <thead> |
- <tr> |
- <th></th> |
- <th class="un">undefined</th> |
- <th class="ob">null</th> |
- <th class="bl">true</th> |
- <th class="bl">false</th> |
- <th class="ob">new Object()</th> |
- <th class="fn">function(){<br> return;<br>}</th> |
- </tr> |
- </thead> |
- <tbody> |
- <tr> |
- <th>!!col</th> |
- <td class="false">false</td> |
- <td class="false">false</td> |
- <td class="true">true</td> |
- <td class="false">false</td> |
- <td class="true">true</td> |
- <td class="true">true</td> |
- </tr> |
- </tbody> |
-</table> |
- |
-<p id="tcBool_6"> |
-For the other types, <code>undefined</code> and <code>null</code> are |
-converted to false, boolean values are not converted and objects and |
-functions are always true. |
-</p> |
- |
-<p id="tcBool_7"> |
-This is the most valuable aspect of type-converting to boolean as it |
-allows a script to distinguish between properties in an environment |
-that may be undefined or may refer to an object. Treating an undefined |
-(or null) value as if it was an object will produce errors. So when |
-there is a doubt (as there usually is where web browsers are concerned) |
-then code can avoid generating errors by wrapping the code that wants |
-to access an object in an <code>if</code> test. Supplying the suspect |
-reference to the object as the expression. The expression will be type |
-converted to boolean and result in <code>false</code> if the object |
-does not exist and <code>true</code> if it does. |
-</p> |
- |
-<pre id="tcBool_ex3"> |
-if(document.documentElement){ |
- scrollX = document.documentElement.scrollLeft; |
-} |
-</pre> |
- |
-<p id="tcBool_8"> |
-The double NOT operation also allows the setting of boolean flags that |
-can be used to indicate the presence of various objects:- |
-</p> |
- |
-<pre id="tcBool_ex4"> |
-var hasDocEl = !!document.documentElement; |
-... |
-if(hasDocEl){ |
- scrollX = document.documentElement.scrollLeft; |
-} |
-</pre> |
- |
-<h2 id="tcString">Converting to String</h2> |
- |
-<p id="tcString_1"> |
-As mentioned above, type conversion to a string most often results |
-from the action of the + operator whenever one of its operators in |
-not a number. The easiest way of getting the string that results |
-from type-conversion is to concatenate a value to an empty string. |
-That technique has been used to generate the following tables. |
-</p> |
- |
-<p id="tcString_2"> |
-An alternative method of converting a value into a string is to |
-pass it as an argument to the <code>String</code> constructor |
-called as a function:- |
-</p> |
- |
-<pre id="tcString_ex1"> |
-var stringValue = String(x); |
-</pre> |
- |
-<table> |
- <caption>type-convert to string ("" + col) : Numeric Values.</caption> |
- <thead> |
- <tr> |
- <th></th> |
- <th class="nm">-1.6</th> |
- <th class="nm">-0</th> |
- <th class="nm">+0</th> |
- <th class="nm">1</th> |
- <th class="nm">1.6</th> |
- <th class="nm">8</th> |
- <th class="nm">16</th> |
- <th class="nm">16.8</th> |
- <th class="nm">123e-2</th> |
- <th class="nm">-Infinity</th> |
- <th class="nm">+Infinity</th> |
- <th class="nm">NaN</th> |
- </tr> |
- </thead> |
- <tbody> |
- <tr> |
- <th>"" + col</th> |
- <td class="st">-1.6</td> |
- <td class="st">0</td> |
- <td class="st">0</td> |
- <td class="st">1</td> |
- <td class="st">1.6</td> |
- <td class="st">8</td> |
- <td class="st">16</td> |
- <td class="st">16.8</td> |
- <td class="st">1.23</td> |
- <td class="st">-Infinity</td> |
- <td class="st">Infinity</td> |
- <td class="st">NaN</td> |
- </tr> |
- </tbody> |
-</table> |
- |
-<p id="tcString_3"> |
-Notice that the number generated from the source code <code>123e-2</code> |
-has resulted in the string <code>"1.23"</code> because that is |
-the string representation of the internal number created from the source |
-code. However, Javascript's internal number representations take the form |
-of IEEE double precision floating point numbers and that means that they |
-cannot represent all numbers with precision. The results of mathematical |
-operations may only produce close approximations and when they are |
-converted to strings the string represents the approximation and may be |
-unexpected and undesirable. It is often necessary to use custom functions |
-to generate string representations of numbers in an acceptable format, |
-the type-conversion mechanism is rarely suited to generating numeric output |
-intended for presentation. |
-</p> |
- |
-<table> |
- <caption>type-convert to string ("" + col) : Other Values.</caption> |
- <thead> |
- <tr> |
- <th></th> |
- <th class="un">undefined</th> |
- <th class="ob">null</th> |
- <th class="bl">true</th> |
- <th class="bl">false</th> |
- <th class="ob">new Object()</th> |
- <th class="fn">function(){<br> return;<br>}</th> |
- </tr> |
- </thead> |
- <tbody> |
- <tr> |
- <th>"" + col</th> |
- <td class="st">undefined</td> |
- <td class="st">null</td> |
- <td class="st">true</td> |
- <td class="st">false</td> |
- <td class="st">[object Object]</td> |
- <td><pre class="st">function(){ |
- return; |
-}</pre></td> |
- </tr> |
- </tbody> |
-</table> |
- |
-<p id="tcString_4"> |
-When objects or functions are type-converted to strings their |
-<code>toString</code> method is called. These default to |
-<code>Object.prototype.toString</code> and |
-<code>Function.prototype.toString</code> but may be overloaded |
-with a function assigned to a "toString" property of |
-the object/function. Type-converting a function to a string does |
-not necessarily result in the function's source code. The behaviour |
-of <code>Function.prototype.toString</code> is implementation |
-depended and varies quite a lot, as do the results from |
-"host objects" and methods (the objects and methods |
-provided by the environment, such as DOM elements). |
-</p> |
- |
-<h2 id="tcNumber">Converting to Number</h2> |
- |
-<p id="tcNumber_1"> |
-Converting values to numbers, especially strings to numbers, is an |
-extremely common requirement and many methods can be used. Any |
-mathematical operator except the concatenation/addition operator |
-will force type-conversion. So conversion of a string to a number |
-might entail performing a mathematical operation on the string |
-representation of the number that would not affect the resulting |
-number, such as subtracting zero or multiplying by one. |
-</p> |
- |
-<pre id="tcNumber_ex1"> |
-var numValue = stringValue - 0; |
-<span class="commentJS">/* or */</span> |
-var numValue = stringValue * 1; |
-<span class="commentJS">/* or */</span> |
-var numValue = stringValue / 1; |
-</pre> |
- |
-<p id="tcNumber_2"> |
-However, the unary <code>+</code> operator also type-converts its |
-operand to a number and because it does not do any additional |
-mathematical operations it is the fastest method for type-converting |
-a string into a number. |
-</p> |
- |
-<p id="tcNumber_2b"> |
-Incidentally, the unary <code>-</code> (minus) operator also |
-type-converts its operand (if necessary) in addition to |
-subsequently negating its value. |
-</p> |
- |
-<pre id="tcNumber_ex2"> |
-var numValue = (+stringValue); |
- |
-<span class="commentJS">/* The preceding unary + expression has been parenthesised. That is |
- unnecessary but is often felt to make the code easier to comprehend |
- and make it clear which operations are being applied. Especially |
- avoiding confusion with pre and post increment and addition |
- operations. Compare:- |
- |
-var n = anyNumVar++ + +stringVar + ++anotherNumVar; |
- |
- - with - |
- |
-var n = (anyNumVar++) + (+stringVar) + (++anotherNumVar); |
- ^^ ^ ^^ |
- (post increment) + (unary plus) + (pre increment) |
-*/</span> |
-</pre> |
- |
-<p id="tcNumber_3"> |
-While unary <code>+</code> is the fastest method for converting a |
-string to a number a final method is available that uses the |
-Javascript type-conversion algorithms. The <code>Number</code> |
-constructor can be called with the string value as its argument |
-and its return value is a number representing the result of the |
-type-conversion. |
-</p> |
- |
-<pre id="tcNumber_ex3"> |
-var numValue = Number(stringValue); |
-</pre> |
- |
-<p id="tcNumber_4"> |
-The Number constructor is the slowest of the type-converting |
-methods but when speed is not an overriding consideration its |
-use does produce the clearest source code. |
-</p> |
- |
-<p id="tcNumber_5"> |
-The following tables show the results of type-conversion to a number using |
-the unary <code>+</code> operator. Though all of the preceding |
-alternative method produce the same results as they all use exactly the |
-same algorithm to do the conversion. |
-</p> |
- |
-<table> |
- <caption>type-convert to number (+col) : String Values.</caption> |
- <thead> |
- <tr> |
- <th></th> |
- <th class="st">""<br>(empty<br>string)</th> |
- <th class="st">"-1.6"</th> |
- <th class="st">"0"</th> |
- <th class="st">"1"</th> |
- <th class="st">"1.6"</th> |
- <th class="st">"8"</th> |
- <th class="st">"16"</th> |
- <th class="st">"16.8"</th> |
- <th class="st">"123e-2"</th> |
- <th class="st">"010"<br>(Octal)</th> |
- <th class="st">"0x10"<br>(Hex)</th> |
- <th class="st">"0xFF"<br>(Hex)</th> |
- <th class="st">"-010"</th> |
- <th class="st">"-0x10"</th> |
- <th class="st">"xx"</th> |
- </tr> |
- </thead> |
- <tbody> |
- <tr> |
- <th>+col</th> |
- <td class="nm">0</td> |
- <td class="nm">-1.6</td> |
- <td class="nm">0</td> |
- <td class="nm">1</td> |
- <td class="nm">1.6</td> |
- <td class="nm">8</td> |
- <td class="nm">16</td> |
- <td class="nm">16.8</td> |
- <td class="nm">1.23</td> |
- <td class="nm">10</td> |
- <td class="nm">16</td> |
- <td class="nm">255</td> |
- <td class="nm">-10</td> |
- <td class="nm">NaN</td> |
- <td class="nm">NaN</td> |
- </tr> |
- </tbody> |
-</table> |
- |
-<p id="tcNumber_6"> |
-The important considerations when converting strings to numbers with |
-the type-converting methods is the results from strings that do not |
-represent numbers. The empty string is converted into the number zero, |
-depending on the application this can be harmless or disastrous, but |
-it is important to be aware that it is going to happen. In other |
-contexts strings that follow the Javascript format for octal number |
-(leading zero) can be problematic but type conversion treats them |
-as base 10 anyway. However, strings that follow the format for |
-hexadecimal numbers (leading <code>0x</code> or <code>0X</code>) |
-are read as hexadecimal. Strings that cannot be read as a number |
-type-convert to <code>NaN</code>, which can be tested for with |
-the <code>isNaN</code> function. Strings representing numbers in an |
-exponential format (<code>"123e-2"</code>) are understood |
-along with leading minus signs. |
-</p> |
- |
-<table> |
- <caption>type-convert to number (+col) : Other Values.</caption> |
- <thead> |
- <tr> |
- <th></th> |
- <th class="un">undefined</th> |
- <th class="ob">null</th> |
- <th class="bl">true</th> |
- <th class="bl">false</th> |
- <th class="ob">new Object()</th> |
- <th class="fn">function(){<br> return;<br>}</th> |
- </tr> |
- </thead> |
- <tbody> |
- <tr> |
- <th>+col</th> |
- <td class="nm">NaN</td> |
- <td class="nm">0</td> |
- <td class="nm">1</td> |
- <td class="nm">0</td> |
- <td class="nm">NaN</td> |
- <td class="nm">NaN</td> |
- </tr> |
- </tbody> |
-</table> |
- |
-<p id="tcNumber_7"> |
-Objects and functions always type-convert to <code>NaN</code> numbers, as do |
-<code>undefined</code> values but it is worth noting that <code>null</code> |
-type-converts to zero. Probably because it is being type-converted to boolean |
-first and then to number and, as is clear from the boolean results |
-above, <code>null</code> would become boolean <code>false</code> which |
-would then become numeric zero. There is almost no need to type convert |
-these types of values into numbers. How they convert is only really |
-relevant to a consideration of the accidental result of converting a |
-value that is expected to be a string but actually turns out to be one |
-of these (and/or performing an mathematical operation with one of these as an operand). |
-</p> |
- |
-<h2 id="tcParse">Parsing to Number</h2> |
- |
-<p id="tcParse_1"> |
-An alternative method of converting a string into a number is to use |
-one of the global functions designed to parse a string and return a |
-number. The <code>parseFloat</code> function accepts a string argument |
-and returns a floating point number resulting from parsing that string. |
-Non-string arguments are first type-converted to a string as described |
-above. |
-</p> |
- |
-<p id="tcParse_2"> |
-The string parsing functions read the string character by character until |
-they encounter a character that cannot be part of the number, at which |
-point they stop and return a number based on the characters that they |
-have seen that can be part of the number. This feature of their action |
-can be usefully exploited, for example, given a string representing a |
-<span class="initialism" title="Cascading Style Sheet"><abbr title="Cascading Style Sheet">CSS</abbr></span> length value such as <code>"34.5em"</code> |
-<code>parseFloat</code> would be able to ignore the <code>"em"</code> |
-because those characters cannot be combined with the preceding set to |
-produce a valid number. The returned number would be 34.5, the numeric |
-part of the <span class="initialism" title="Cascading Style Sheet"><abbr title="Cascading Style Sheet">CSS</abbr></span> string stripped of its units. |
-</p> |
- |
-<h3 id="tcParseFl">parseFloat</h3> |
- |
-<table> |
- <caption>parseFloat(col) : String Values.</caption> |
- <thead> |
- <tr> |
- <th></th> |
- <th class="st">""<br>(empty<br>string)</th> |
- <th class="st">"-1.6"</th> |
- <th class="st">"0"</th> |
- <th class="st">"1"</th> |
- <th class="st">"1.6"</th> |
- <th class="st">"8"</th> |
- <th class="st">"16"</th> |
- <th class="st">"16.8"</th> |
- <th class="st">"123e-2"</th> |
- <th class="st">"010"<br>(Octal)</th> |
- <th class="st">"0x10"<br>(Hex)</th> |
- <th class="st">"0xFF"<br>(Hex)</th> |
- <th class="st">"-010"</th> |
- <th class="st">"-0x10"</th> |
- <th class="st">"xx"</th> |
- </tr> |
- </thead> |
- <tbody> |
- <tr> |
- <th>parseFloat(col)</th> |
- <td class="nm">NaN</td> |
- <td class="nm">-1.6</td> |
- <td class="nm">0</td> |
- <td class="nm">1</td> |
- <td class="nm">1.6</td> |
- <td class="nm">8</td> |
- <td class="nm">16</td> |
- <td class="nm">16.8</td> |
- <td class="nm">1.23</td> |
- <td class="nm">10</td> |
- <td class="nm">0</td> |
- <td class="nm">0</td> |
- <td class="nm">-10</td> |
- <td class="nm">0</td> |
- <td class="nm">NaN</td> |
- </tr> |
- </tbody> |
-</table> |
- |
-<p id="tcParseFl_1"> |
-With <code>parseFloat</code> empty strings return <code>NaN </code> |
-along with strings that cannot be subject to numeric interpretation. |
-The exponential format is understood and the leading zero in the |
-octal format does not hinder the string's interpretation as a |
-decimal number. Hexadecimal strings are interpreted as the number |
-zero because the following <code>"x"</code> cannot be |
-interpreted as part of a number so parsing stops after the leading zero. |
-</p> |
- |
-<table> |
- <caption>parseFloat(col) : Other Values.</caption> |
- <thead> |
- <tr> |
- <th></th> |
- <th class="un">undefined</th> |
- <th class="ob">null</th> |
- <th class="bl">true</th> |
- <th class="bl">false</th> |
- <th class="ob">new Object()</th> |
- <th class="fn">function(){<br> return;<br>}</th> |
- </tr> |
- </thead> |
- <tbody> |
- <tr> |
- <th>parseFloat(col)</th> |
- <td class="nm">NaN</td> |
- <td class="nm">NaN</td> |
- <td class="nm">NaN</td> |
- <td class="nm">NaN</td> |
- <td class="nm">NaN</td> |
- <td class="nm">NaN</td> |
- </tr> |
- </tbody> |
-</table> |
- |
-<p id="tcParseFl_2"> |
-Non-string values are first converted into a string that is employed |
-by <code>parseFloat</code>. As that type-conversion to a string would |
-not normally result in a string that could be interpreted as a number |
-the result is <code>NaN</code>. Objects and functions may have custom |
-<code>toString</code> methods that may return strings that could be |
-interpreted as numbers but that would be an unusual requirement. |
-</p> |
- |
-<h3 id="tcParseIn">parseInt</h3> |
- |
-<p id="tcParseIn_1"> |
-The <code>parseInt</code> function works in a similar way to |
-<code>parseFloat</code> except that it is trying to interpret its |
-string argument into an integer and as a result recognises fewer |
-character as possible candidates to be part of that number. |
-</p> |
- |
-<p id="tcParseIn_2"> |
-<code>parseInt</code> is occasionally used as a means of turning a |
-floating point number into an integer. It is very ill suited to that |
-task because if its argument is of numeric type it will first be |
-converted into a string and then parsed as a number, very inefficient. |
-This can produce very wrong results with numbers such as |
-<code>2e-200</code>, for which the next smaller integer is zero, but |
-with which <code>parseInt</code> returns <code>2</code>. Also, because |
-of the number format used by javascript, numbers are often represented |
-by near approximations. So, for example, 1/2 + 1/3 + 1/6 = |
-0.9999999999999999, which isn't quite one and parseInt would return |
-zero if asked to act on the result of the operation. |
-</p> |
- |
-<p id="tcParseIn_3"> |
-For rounding |
-numbers to integers one of <code>Math.round</code>, <code>Math.ceil</code> |
-and <code>Math.floor</code> are preferable, and for a desired result |
-that can be expressed as a 32 bit signed integer the bitwise operation |
-described below might also suit. |
-</p> |
- |
-<table> |
- <caption>parseInt(col) : Numeric Values.</caption> |
- <thead> |
- <tr> |
- <th></th> |
- <th class="nm">-1.6</th> |
- <th class="nm">-0</th> |
- <th class="nm">+0</th> |
- <th class="nm">1</th> |
- <th class="nm">1.6</th> |
- <th class="nm">8</th> |
- <th class="nm">16</th> |
- <th class="nm">16.8</th> |
- <th class="nm">123e-2</th> |
- <th class="nm">-Infinity</th> |
- <th class="nm">+Infinity</th> |
- <th class="nm">NaN</th> |
- </tr> |
- </thead> |
- <tbody> |
- <tr> |
- <th>parseInt(col)</th> |
- <td class="nm">-1</td> |
- <td class="nm">0</td> |
- <td class="nm">0</td> |
- <td class="nm">1</td> |
- <td class="nm">1</td> |
- <td class="nm">8</td> |
- <td class="nm">16</td> |
- <td class="nm">16</td> |
- <td class="nm">1</td> |
- <td class="nm">NaN</td> |
- <td class="nm">NaN</td> |
- <td class="nm">NaN</td> |
- </tr> |
- </tbody> |
-</table> |
- |
-<p id="tcParseIn_4"> |
-When it is acting on number the effect of the initial type-conversion |
-of the argument to a string is evident in the results. Note that the |
-value <code>123e-2</code> is internally the number <code>1.23</code> |
-and that type converts into the string <code>"1.23"</code>, |
-so that entry in the table above might look odd but it is correct. |
-</p> |
- |
-<table> |
- <caption>parseInt(col) : String Values.</caption> |
- <thead> |
- <tr> |
- <th></th> |
- <th class="st">""<br>(empty<br>string)</th> |
- <th class="st">"-1.6"</th> |
- <th class="st">"0"</th> |
- <th class="st">"1"</th> |
- <th class="st">"1.6"</th> |
- <th class="st">"8"</th> |
- <th class="st">"16"</th> |
- <th class="st">"16.8"</th> |
- <th class="st">"123e-2"</th> |
- <th class="st">"010"<br>(Octal)</th> |
- <th class="st">"0x10"<br>(Hex)</th> |
- <th class="st">"0xFF"<br>(Hex)</th> |
- <th class="st">"-010"</th> |
- <th class="st">"-0x10"</th> |
- <th class="st">"xx"</th> |
- </tr> |
- </thead> |
- <tbody> |
- <tr> |
- <th>parseInt(col)</th> |
- <td class="nm">NaN</td> |
- <td class="nm">-1</td> |
- <td class="nm">0</td> |
- <td class="nm">1</td> |
- <td class="nm">1</td> |
- <td class="nm">8</td> |
- <td class="nm">16</td> |
- <td class="nm">16</td> |
- <td class="nm">123</td> |
- <td class="nm">8</td> |
- <td class="nm">16</td> |
- <td class="nm">255</td> |
- <td class="nm">-8</td> |
- <td class="nm">-16</td> |
- <td class="nm">NaN</td> |
- </tr> |
- </tbody> |
-</table> |
- |
-<p id="tcParseIn_5"> |
-Strings in octal and hexadecimal number formats do represent integers |
-and <code>parseInt</code> is capable of interpreting them in accordance |
-with the rules for Javascript source code, even when they have leading |
-minus signs. |
-</p> |
- |
-<table> |
- <caption>parseInt(col) : Other Values.</caption> |
- <thead> |
- <tr> |
- <th></th> |
- <th class="un">undefined</th> |
- <th class="ob">null</th> |
- <th class="bl">true</th> |
- <th class="bl">false</th> |
- <th class="ob">new Object()</th> |
- <th class="fn">function(){<br> return;<br>}</th> |
- </tr> |
- </thead> |
- <tbody> |
- <tr> |
- <th>parseInt(col)</th> |
- <td class="nm">NaN</td> |
- <td class="nm">NaN</td> |
- <td class="nm">NaN</td> |
- <td class="nm">NaN</td> |
- <td class="nm">NaN</td> |
- <td class="nm">NaN</td> |
- </tr> |
- </tbody> |
-</table> |
- |
-<p id="tcParseIn_6"> |
-As <code>parseInt</code> type-converts its non-string arguments to |
-strings it always produces the same results for <code>boolean</code>, |
-<code>null</code>, <code>undefined</code>, object and function |
-arguments as <code>parseFloat</code> (assuming objects and functions |
-do not have custom <code>toString</code> methods). |
-</p> |
- |
-<h3 id="tcPrIntRx">parseInt with a radix argument</h3> |
- |
-<p id="tcPrIntRx_1"> |
-It is rarely desirable to allow <code>parseInt</code> to deduce the |
-base in which the number is represented from the string as leading zeros are |
-rarely intended to indicate data in octal format (particularly with |
-user input). To deal with this problem <code>parseInt</code> recognises |
-a second, radix, argument that can be used to specify the base in which the |
-string is to be interpreted. Specifying a second argument of 10 causes |
-<code>parseInt</code> to interpret the strings as only base 10. |
-</p> |
- |
-<table> |
- <caption>parseInt(col, 10) : String Values.</caption> |
- <thead> |
- <tr> |
- <th></th> |
- <th class="st">""<br>(empty<br>string)</th> |
- <th class="st">"-1.6"</th> |
- <th class="st">"0"</th> |
- <th class="st">"1"</th> |
- <th class="st">"1.6"</th> |
- <th class="st">"8"</th> |
- <th class="st">"16"</th> |
- <th class="st">"16.8"</th> |
- <th class="st">"123e-2"</th> |
- <th class="st">"010"<br>(Octal)</th> |
- <th class="st">"0x10"<br>(Hex)</th> |
- <th class="st">"0xFF"<br>(Hex)</th> |
- <th class="st">"-010"</th> |
- <th class="st">"-0x10"</th> |
- <th class="st">"xx"</th> |
- </tr> |
- </thead> |
- <tbody> |
- <tr> |
- <th>parseInt(col, 10)</th> |
- <td class="nm">NaN</td> |
- <td class="nm">-1</td> |
- <td class="nm">0</td> |
- <td class="nm">1</td> |
- <td class="nm">1</td> |
- <td class="nm">8</td> |
- <td class="nm">16</td> |
- <td class="nm">16</td> |
- <td class="nm">123</td> |
- <td class="nm">10</td> |
- <td class="nm">0</td> |
- <td class="nm">0</td> |
- <td class="nm">-10</td> |
- <td class="nm">0</td> |
- <td class="nm">NaN</td> |
- </tr> |
- </tbody> |
-</table> |
- |
-<p id="tcPrIntRx_2"> |
-The string in octal format is now interpreted as base 10 and the |
-hexadecimal strings can now only be zero as parsing has to stop |
-when the <code>"x"</code> is encountered. |
-</p> |
- |
-<p id="tcPrIntRx_3"> |
-Number bases 2 to 36 can be used with <code>parseInt</code>. The |
-following is base 16. |
-</p> |
- |
-<table> |
- <caption>parseInt(col, 16) : String Values.</caption> |
- <thead> |
- <tr> |
- <th></th> |
- <th class="st">""<br>(empty<br>string)</th> |
- <th class="st">"-1.6"</th> |
- <th class="st">"0"</th> |
- <th class="st">"1"</th> |
- <th class="st">"1.6"</th> |
- <th class="st">"8"</th> |
- <th class="st">"16"</th> |
- <th class="st">"16.8"</th> |
- <th class="st">"123e-2"</th> |
- <th class="st">"010"<br>(Octal)</th> |
- <th class="st">"0x10"<br>(Hex)</th> |
- <th class="st">"0xFF"<br>(Hex)</th> |
- <th class="st">"-010"</th> |
- <th class="st">"-0x10"</th> |
- <th class="st">"xx"</th> |
- </tr> |
- </thead> |
- <tbody> |
- <tr> |
- <th>parseInt(col, 16)</th> |
- <td class="nm">NaN</td> |
- <td class="nm">-1</td> |
- <td class="nm">0</td> |
- <td class="nm">1</td> |
- <td class="nm">1</td> |
- <td class="nm">8</td> |
- <td class="nm">22</td> |
- <td class="nm">22</td> |
- <td class="nm">4670</td> |
- <td class="nm">16</td> |
- <td class="nm">16</td> |
- <td class="nm">255</td> |
- <td class="nm">-16</td> |
- <td class="nm">-16</td> |
- <td class="nm">NaN</td> |
- </tr> |
- </tbody> |
-</table> |
- |
-<p id="tcPrIntRx_4"> |
-The hexadecimal <code>0x</code> format is recognised again with the |
-base 16 interpretation. |
-</p> |
- |
-<p id="tcPrIntRx_5"> |
-Finally base 3:- |
-</p> |
- |
-<table> |
- <caption>parseInt(col, 3) : Numeric Values.</caption> |
- <thead> |
- <tr> |
- <th></th> |
- <th class="nm">-1.6</th> |
- <th class="nm">-0</th> |
- <th class="nm">+0</th> |
- <th class="nm">1</th> |
- <th class="nm">1.6</th> |
- <th class="nm">8</th> |
- <th class="nm">16</th> |
- <th class="nm">16.8</th> |
- <th class="nm">123e-2</th> |
- <th class="nm">-Infinity</th> |
- <th class="nm">+Infinity</th> |
- <th class="nm">NaN</th> |
- </tr> |
- </thead> |
- <tbody> |
- <tr> |
- <th>parseInt(col, 3)</th> |
- <td class="nm">-1</td> |
- <td class="nm">0</td> |
- <td class="nm">0</td> |
- <td class="nm">1</td> |
- <td class="nm">1</td> |
- <td class="nm">NaN</td> |
- <td class="nm">1</td> |
- <td class="nm">1</td> |
- <td class="nm">1</td> |
- <td class="nm">NaN</td> |
- <td class="nm">NaN</td> |
- <td class="nm">NaN</td> |
- </tr> |
- </tbody> |
-</table> |
- |
-<p id="tcPrIntRx_6"> |
-The consequences of the type-converting of numeric arguments to |
-strings is evident again. The number <code>8</code> is coming out |
-as <code>NaN</code> because the <code>"8"</code> character |
-cannot be interpreted as base 3, leaving an empty sequence of |
-acceptable characters and producing the same result as an empty string. |
-</p> |
- |
-<table> |
- <caption>parseInt(col, 3) : String Values.</caption> |
- <thead> |
- <tr> |
- <th></th> |
- <th class="st">""<br>(empty<br>string)</th> |
- <th class="st">"-1.6"</th> |
- <th class="st">"0"</th> |
- <th class="st">"1"</th> |
- <th class="st">"1.6"</th> |
- <th class="st">"8"</th> |
- <th class="st">"16"</th> |
- <th class="st">"16.8"</th> |
- <th class="st">"123e-2"</th> |
- <th class="st">"010"<br>(Octal)</th> |
- <th class="st">"0x10"<br>(Hex)</th> |
- <th class="st">"0xFF"<br>(Hex)</th> |
- <th class="st">"-010"</th> |
- <th class="st">"-0x10"</th> |
- <th class="st">"xx"</th> |
- </tr> |
- </thead> |
- <tbody> |
- <tr> |
- <th>parseInt(col, 3)</th> |
- <td class="nm">NaN</td> |
- <td class="nm">-1</td> |
- <td class="nm">0</td> |
- <td class="nm">1</td> |
- <td class="nm">1</td> |
- <td class="nm">NaN</td> |
- <td class="nm">1</td> |
- <td class="nm">1</td> |
- <td class="nm">5</td> |
- <td class="nm">3</td> |
- <td class="nm">0</td> |
- <td class="nm">0</td> |
- <td class="nm">-3</td> |
- <td class="nm">0</td> |
- <td class="nm">NaN</td> |
- </tr> |
- </tbody> |
-</table> |
- |
-<h2 id="tcToInt32">ToInt32</h2> |
- |
-<p id="tcToInt32_1"> |
-<code>ToInt32</code> is an <em>internal</em> function only available to the |
-Javascript implementation and cannot be called directly from scripts |
-in the way that <code>parseInt</code> can. It is a bit unusual to mention it in |
-connection with converting Javascript values to numbers but it can |
-be used in a limited set of circumstances. The bitwise operators such |
-as bitwise OR (<code>|</code>) and bitwise AND (<code>&</code>) operate on |
-numbers so they type-convert their operands to numbers. However, they |
-also only operate on 32 bit signed integers so given the (possibly |
-type-converted) numeric value they call the <em>internal</em> |
-<code>ToInt32</code> function with that number as its argument and |
-use the returned value as their operand. That returned value is always |
-a 32 bit signed integer. |
-</p> |
- |
-<p id="tcToInt32_2"> |
-The effect can be like <code>parseInt</code> combined with type-converting |
-to numbers. While the result is limited in range to 32 bits, it is |
-<em>always</em> numeric and never <code>NaN</code>, or ± |
-<code>Infinity</code>. |
-</p> |
- |
-<p id="tcToInt32_3"> |
-As with using mathematical operators in operations that have no effect on |
-the value of any resulting number it is possible to perform a bitwise |
-operation that will not affect the value returned from the call to |
-<code>ToInt32</code>. The tables below were generated using a bitwise |
-OR zero operation. |
-</p> |
- |
-<table> |
- <caption> ToInt32 (col|0) : Numeric Values.</caption> |
- <thead> |
- <tr> |
- <th></th> |
- <th class="nm">-1.6</th> |
- <th class="nm">-0</th> |
- <th class="nm">+0</th> |
- <th class="nm">1</th> |
- <th class="nm">1.6</th> |
- <th class="nm">8</th> |
- <th class="nm">16</th> |
- <th class="nm">16.8</th> |
- <th class="nm">123e-2</th> |
- <th class="nm">-Infinity</th> |
- <th class="nm">+Infinity</th> |
- <th class="nm">NaN</th> |
- </tr> |
- </thead> |
- <tbody> |
- <tr> |
- <th>col|0</th> |
- <td class="nm">-1</td> |
- <td class="nm">0</td> |
- <td class="nm">0</td> |
- <td class="nm">1</td> |
- <td class="nm">1</td> |
- <td class="nm">8</td> |
- <td class="nm">16</td> |
- <td class="nm">16</td> |
- <td class="nm">1</td> |
- <td class="nm">0</td> |
- <td class="nm">0</td> |
- <td class="nm">0</td> |
- </tr> |
- </tbody> |
-</table> |
- |
-<p id="tcToInt32_4"> |
-<code>NaN</code> and ±<code>Infinity</code> become zero and |
-floating point values are <em>truncated</em> to integers. |
-</p> |
- |
-<table> |
- <caption> ToInt32 (col|0) : String Values.</caption> |
- <thead> |
- <tr> |
- <th></th> |
- <th class="st">""<br>(empty<br>string)</th> |
- <th class="st">"-1.6"</th> |
- <th class="st">"0"</th> |
- <th class="st">"1"</th> |
- <th class="st">"1.6"</th> |
- <th class="st">"8"</th> |
- <th class="st">"16"</th> |
- <th class="st">"16.8"</th> |
- <th class="st">"123e-2"</th> |
- <th class="st">"010"<br>(Octal)</th> |
- <th class="st">"0x10"<br>(Hex)</th> |
- <th class="st">"0xFF"<br>(Hex)</th> |
- <th class="st">"-010"</th> |
- <th class="st">"-0x10"</th> |
- <th class="st">"xx"</th> |
- </tr> |
- </thead> |
- <tbody> |
- <tr> |
- <th>col|0</th> |
- <td class="nm">0</td> |
- <td class="nm">-1</td> |
- <td class="nm">0</td> |
- <td class="nm">1</td> |
- <td class="nm">1</td> |
- <td class="nm">8</td> |
- <td class="nm">16</td> |
- <td class="nm">16</td> |
- <td class="nm">1</td> |
- <td class="nm">10</td> |
- <td class="nm">16</td> |
- <td class="nm">255</td> |
- <td class="nm">-10</td> |
- <td class="nm">0</td> |
- <td class="nm">0</td> |
- </tr> |
- </tbody> |
-</table> |
- |
-<p id="tcToInt32_5"> |
-String values that would type-convert to <code>NaN</code> are returned |
-as zero from <code>ToInt32</code>. |
-</p> |
- |
-<table> |
- <caption> ToInt32 (col|0) : Other Values.</caption> |
- <thead> |
- <tr> |
- <th></th> |
- <th class="un">undefined</th> |
- <th class="ob">null</th> |
- <th class="bl">true</th> |
- <th class="bl">false</th> |
- <th class="ob">new Object()</th> |
- <th class="fn">function(){<br> return;<br>}</th> |
- </tr> |
- </thead> |
- <tbody> |
- <tr> |
- <th>col|0</th> |
- <td class="nm">0</td> |
- <td class="nm">0</td> |
- <td class="nm">1</td> |
- <td class="nm">0</td> |
- <td class="nm">0</td> |
- <td class="nm">0</td> |
- </tr> |
- </tbody> |
-</table> |
- |
-<p id="tcToInt32_6"> |
-Even <code>undefined</code>, objects and functions are converted to zero value |
-numbers by this operation. Note though that boolean <code>true</code> is |
-converted to the number 1. |
-</p> |
- |
- |
-<h2 id="tcUserIn">Converting User Input</h2> |
- |
-<p id="tcUserIn_1"> |
-Most of the mechanisms for getting input from the user, |
-<code><input type="text"></code> and |
-<code>prompt</code> for example, provide their results in the form |
-of strings. If the user is expected to input a number they still |
-might enter anything (at the least they may just make a typo). If |
-the string needs to be converted into a number for later operations |
-one of the methods mentioned above can be chosen based on what best |
-suits the nature of the input expected but some of the results |
-generated with erroneous input may be difficult to detect and handle. |
-</p> |
- |
-<p id="tcUserIn_2"> |
-Prior to converting a string to a number it may be advantageous |
-to use a Regular Expression to test the contents of the string |
-to ensure that they conform to an acceptable format. That would |
-serve to eliminate some of the string values that may otherwise |
-suffer from the quirks of the string to number converting |
-processes when applied to unexpected string values. |
-</p> |
- |
- |
-<h3 id="tcRegEx">Regular expression examples</h3> |
- |
-<pre id="tcRegExEm"> |
-/^\d+$/ <span class="commentJS">//All-digit</span> |
-/^\s*[-+]?\d+\s*$/ <span class="commentJS">//Unbroken Signed integer & spaces</span> |
-/^\d{1,5}$/ <span class="commentJS">//1 to 5 digits</span> |
-/^\d+\.\d\d$/ <span class="commentJS">//Money</span> |
-/^\d+(\.\d{2})$/ <span class="commentJS">//Money</span> |
-/^\d{1,3}(,\d\d\d)*\.\d\d$/ <span class="commentJS">//comma-separated money - 12,432.57</span> |
- |
- <span class="commentJS">// optional comma-separated money - 12,432.57 or 12432.57</span> |
-/^\d{1,3}(,\d\d\d)*\.\d\d$|^\d+\.\d\d$/ |
- |
-</pre> |
-</body> |
-</html> |
/cljs/faq_notes/type_convert.html |
Property changes: |
Deleted: svn:mime-type |
## -1 +0,0 ## |
-text/plain |
\ No newline at end of property |
Index: cljs/faq_notes/form_access.html |
=================================================================== |
--- cljs/faq_notes/form_access.html (revision 43) |
+++ cljs/faq_notes/form_access.html (nonexistent) |
@@ -1,729 +0,0 @@ |
-<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" |
- "http://www.w3.org/TR/html4/strict.dtd"> |
-<html lang="en"> |
-<head><title>Referencing Forms and Form Controls</title> |
-<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1"> |
-<link href="../../faq.css" rel="stylesheet" type="text/css"> |
-<link href="../notes.css" rel="stylesheet" type="text/css"> |
-<style type="text/css"> |
-.commentJS { |
- background-color: #FFFFCC; |
- color: #004800; |
-} |
-P CODE { |
- background-color: #FFFFDD; |
- color: #000000; |
- padding: 0ex; |
- margin: 0ex; |
-} |
-</style> |
-</head> |
-<body> |
- |
-<h1 id="faHead">Referencing Forms and Form Controls</h1> |
-<div id="faqNav"> |
- <a href="../../">FAQ</a> > <a href="../">FAQ Notes</a> |
-</div> |
- |
-<ul> |
- <li><a href="#faInt">Introduction</a> |
- <ul> |
- <li><a href="#faInF">Forms</a></li> |
- <li><a href="#faInC">Form Controls</a></li> |
- </ul> |
- </li> |
- <li><a href="#faShrt">Shortcut Accessors</a></li> |
- <li><a href="#faComMis">The Most Common Mistake</a></li> |
- <li><a href="#faAnon">Anonymous Form References</a></li> |
- <li><a href="#faBut">Radio Button and Other Control Collections</a></li> |
- <li><a href="#faEff">Efficient use of Form Accessors</a></li> |
-</ul> |
- |
-<h2 id="faInt">Introduction</h2> |
-<h3 id="faInF">Forms</h3> |
-<p id="faInF_1"> |
-When the W3C defined the <span class="initialism" title="HyperText Mark-up Language"><abbr title="HyperText Mark-up Language">HTML</abbr></span> DOM specification much of what they included |
-represented a formalisation of existing browser behaviour. In particular they |
-defined "convenience" properties on the <code>HTMLDocument</code> |
-interface that reproduce document level collections common in preceding |
-browsers. Of specific interest here is the <code>document.forms</code> |
-collection, which makes all of the <code>FORM</code> elements on a page |
-available as (zero based) indexed members of the collection. Allowing, |
-for example, the second <code>FORM</code> element on a page to be |
-referenced as:- |
-</p> |
- |
-<pre id="faInF_ex1"> |
-var formElement = document.forms[1]; |
-</pre> |
- |
-<p id="faInF_2"> |
-<span class="initialism" title="HyperText Mark-up Language"><abbr title="HyperText Mark-up Language">HTML</abbr></span> (but not necessarily XHTML) <code>FORM</code> elements are allowed |
-<code>NAME</code> attributes and the <code>document.forms</code> collection |
-also makes <code>FORM</code> elements with <code>NAME</code> attributes |
-available as named members, under a property name that corresponds with |
-value of the <code>NAME</code> attribute. So given a form with the |
-attribute <code>name="myForm"</code> the form can be referenced |
-as:- |
-</p> |
- |
-<pre id="faInF_ex2"> |
-var formElement = document.forms.myForm; |
- |
-<span class="commentJS">/* - or - */</span> |
- |
-var formElement = document.forms["myForm"]; |
- |
-<span class="commentJS">/* The latter, bracket notation, does not impose the same restrictions |
- on the character sequence used for the name as is imposed by the |
- preceding dot notation, which is restricted to only using character |
- sequences that would fulfill the ECMAScript definition of an |
- identifier. |
- |
- Bracket notation is often preferred when accessing form elements as |
- it helps to document itself by making it clear in the source code |
- which property names originate in the <span class="initialism" title="HyperText Mark-up Language"><abbr title="HyperText Mark-up Language">HTML</abbr></span> rather than the DOM. |
-*/</span> |
-</pre> |
- |
-<p id="faInF_3"> |
-The <code>document.forms</code> collection had exhibited this behaviour |
-in all of the preceding browsers that implemented it (which included |
-all the browsers that understood what a form was) and as a result |
-represents the most cross-browser method of accessing <code>FORM</code> |
-elements. It is both W3C <span class="initialism" title="HyperText Mark-up Language"><abbr title="HyperText Mark-up Language">HTML</abbr></span> DOM standard compliant and |
-back-compatible with pre-existing browsers. |
-</p> |
- |
-<p id="faInF_4"> |
-The W3C went on to require <code>FORM</code> elements with |
-<code>ID</code> attributes to also be made available as named properties |
-of the <code>document.forms</code> collection. That represented a |
-formalisation of behaviour already exhibited in IE 4 but not by |
-Netscape 4 (and earlier). Referencing <code>ID</code>ed <code>FORM</code> |
-elements as named properties of the <code>document.forms</code> collection |
-should work reliably in all W3C <span class="initialism" title="HyperText Mark-up Language"><abbr title="HyperText Mark-up Language">HTML</abbr></span> DOM compliant browsers but some |
-back-compatibility will be sacrificed if <code>ID</code>s are used instead |
-of <code>NAME</code>s (though not nearly as much as would be lost if |
-<code>ID</code>ed form elements were referenced using the |
-<code>document.getElementById</code> method). |
-</p> |
- |
-<p id="faInF_5"> |
-When writing <span class="initialism" title="HyperText Mark-up Language"><abbr title="HyperText Mark-up Language">HTML</abbr></span> that conforms to a DTD that allows <code>FORM</code> |
-elements to have <code>NAME</code> attributes it is possible to also give |
-the <code>FORM</code> element an <code>ID</code> attribute that corresponds |
-with its <code>NAME</code> attribute (so long as the <code>ID</code> is |
-unique on the page). The form will appear as a member of the |
-<code>document.forms</code> collection under a property name that |
-corresponds with the value of the <code>NAME</code> and <code>ID</code> |
-attributes (as they are identical). |
-</p> |
- |
-<h3 id="faInC">Form Controls</h3> |
- |
-<p id="faInC_1"> |
-Traditionally browsers that implemented the <code>document.forms</code> |
-collection also made the controls within a form available as a |
-collection accessible as a property of the <code>FORM</code> element |
-under the name <code>elements</code>. The W3C <span class="initialism" title="HyperText Mark-up Language"><abbr title="HyperText Mark-up Language">HTML</abbr></span> DOM also formalised |
-this collection in the <code>HTMLFormElement</code> interface. |
-Controls within a form can be referenced as integer indexed members of |
-that collection:- |
-</p> |
- |
-<pre id="faInC_ex1"> |
-var formElement = document.forms["myForm"]; |
-var controlElement = formElement.elements[2]; <span class="commentJS">//Third control in the form.</span> |
-</pre> |
- |
-<p id="faInC_2"> |
-Controlls with <code>NAME</code> attributes are again made available as |
-named properties of the collection. So a control with |
-<code>name="myControl"</code> can be referenced as:- |
-</p> |
- |
-<pre id="faInC_ex2"> |
-var controlElement = formElement.elements["myControl"]; |
-</pre> |
- |
-<p id="faInC_3"> |
-Again the W3C also specified that controls with <code>ID</code> |
-attributes should be made available as named members of the |
-elements collection under their <code>ID</code>s (with the same |
-implications for back-compatibility with really ancient browsers). |
-All official (x)<span class="initialism" title="HyperText Mark-up Language"><abbr title="HyperText Mark-up Language">HTML</abbr></span> DTDs allow form controls to have |
-<code>NAME</code> attributes because without a <code>NAME</code> |
-attribute the value of the control cannot be sent as a |
-name/value pair when the form is submitted. |
-</p> |
- |
-<h2 id="faShrt">Shortcut Accessors</h2> |
- |
-<p id="faShrt_1"> |
-In addition to making named <code>FORM</code> elements available |
-as named properties of the <code>document.forms</code> collection |
-web browsers also make them available as named properties of the |
-<code>document</code> object. So:- |
-</p> |
- |
-<pre id="faShrt_ex1"> |
-var formElement = document.myForm; |
-</pre> |
- |
-<p id="faShrt_2"> |
--will reference the same FORM element as:- |
-</p> |
- |
-<pre id="faShrt_ex2"> |
-var formElement = document.forms.myForm; |
-</pre> |
- |
-<p id="faShrt_3"> |
-And the same is true using bracket notation:- |
-</p> |
- |
-<pre id="faShrt_ex3"> |
-var formElement = document["myForm"]; |
- |
-<span class="commentJS">/* instead of:- */ </span> |
- |
-var formElement = document.forms["myForm"]; |
-</pre> |
- |
-<p id="faShrt_4"> |
-The W3C did not include this shortcut in the <span class="initialism" title="HyperText Mark-up Language"><abbr title="HyperText Mark-up Language">HTML</abbr></span> DOM specifications |
-so code that uses it cannot be described as standards compliant and, |
-while nobody has been able to name an <span class="initialism" title="HyperText Mark-up Language"><abbr title="HyperText Mark-up Language">HTML</abbr></span> browser where the shortcut |
-accessors do not work when referencing named <code>FORM</code> |
-elements, it would still be possible for a future standards compliant |
-browser not to support the shortcut accessors. |
-</p> |
- |
-<p id="faShrt_5"> |
-<code>FORM</code> elements that only have <code>ID</code> attributes |
-cannot be accessed as properties of the <code>document</code> object |
-under a property name that corresponds with their <code>ID</code> |
-attributes. While by W3C <span class="initialism" title="HyperText Mark-up Language"><abbr title="HyperText Mark-up Language">HTML</abbr></span> DOM specification (but not necessarily |
-on older browsers) <code>ID</code>ed forms are made available as named |
-properties of the <code>document.forms</code> collections under a |
-property name that corresponds with the <code>ID</code>. |
-</p> |
- |
-<p id="faShrt_6"> |
-Form controls can be referenced as named properties of the |
-<code>FORM</code> element that contains them with a shortcut accessor:- |
-</p> |
- |
-<pre id="faShrt_ex4"> |
-var formControl = formElement.myControl; |
- |
-<span class="commentJS">/* instead of :- */</span> |
- |
-var formControl = fromElement.elements.myControl; |
-</pre> |
- |
-<p id="faShrt_7"> |
-But in the case of form controls, elements with <code>ID</code> |
-attributes may be available as properties of the <code>FORM</code> |
-element under a property name that corresponds with their |
-<code>ID</code> (at least on more recent browsers). |
-</p> |
- |
-<p id="faShrt_8"> |
-The main argument in favour of using shortcut accessors (apart from the |
-reduced amount of typing) is that they are resolved fractionally quicker |
-than accessors that employ the <code>forms</code> and |
-<code>elements</code> collections. That follows from the fact that fewer |
-object references need to be resolved before the reference to the element |
-of interest is returned. |
-</p> |
- |
-<p id="faShrt_9"> |
-While arguments against the shortcut accessors point out that named image, |
-embed and other elements are also made available as named properties of |
-the <code>document</code> object, making it ambiguous when reading the |
-source code whether the shortcut accessor is referring to a form or some |
-other named property of the <code>document</code> object. When a |
-<code>FORM</code> element is referenced as a member of the |
-<code>document.forms</code> collection, or a control as a member of the |
-<code>elements</code> collection, there can be no doubt while reading |
-the source code as to the type of element that is the subject of the |
-reference. |
-</p> |
- |
-<h2 id="faComMis">The Most Common Mistake</h2> |
- |
-<p id="faComMis_1"> |
-The most common mistake made when defining the form <span class="initialism" title="HyperText Mark-up Language"><abbr title="HyperText Mark-up Language">HTML</abbr></span> that a script |
-will interact with follows from the existence of the shortcut accessors |
-for form controls. It is to give the control a <code>NAME</code> (or |
-possibly <code>ID</code>) that corresponds with an existing property of |
-<code>FORM</code> elements. And the most common example of that is an |
-<code>INPUT</code> element of <code>type="submit"</code> with |
-the <code>NAME</code> "submit". Because the named controls are |
-made available as named properties of the <code>FORM</code> element this |
-<code>INPUT</code> element is made available under the property name |
-<code>"submit"</code>. Unfortunately <code>FORM</code> elements |
-already have a property with the name <code>"submit"</code>, it |
-is the <code>submit</code> method that can be used to submit the form |
-with a script. The misguided choice of name for the <code>INPUT</code> |
-element effectively renders the form's <code>submit</code> method |
-unscriptable. And the same is true for all controls with names that |
-correspond any with existing <code>FORM</code> element properties. |
-</p> |
- |
-<p id="faComMis_2"> |
-Because ECMAScript is case sensitive it may only be necessary to |
-capitalise the name of the <code>INPUT</code> element to avoid the |
-conflict. However, it would probably be safest to adopt a naming |
-convention for form controls that ensured that they do not |
-correspond with existing properties of the <code>FORM</code> elements |
-regardless of case. Especially as theW3C <span class="initialism" title="HyperText Mark-up Language"><abbr title="HyperText Mark-up Language">HTML</abbr></span> specification implies |
-that referring to named properties of collections can be case |
-insensitive in <span class="initialism" title="HyperText Mark-up Language"><abbr title="HyperText Mark-up Language">HTML</abbr></span> DOMs. In reality I don't know of any |
-implementations in which it is but it would be better to err on the side |
-of caution. |
-</p> |
- |
-<p id="faComMis_3"> |
-Another naming conflict that should be avoided would arise if named form |
-controls had names that correspond with the string representations of |
-integers, such as <code>name="1"</code> as they will almost |
-certainly result in inconsistent results (across browsers) when being |
-referenced because the controls are already available by integer index |
-and it would become unclear which control was being referenced by |
-accessors such as <code>formElement.elements[1]</code> or |
-<code>formElement.elements["1"]</code> (by the ECMAScript |
-specification the preceding two property accessors are equivalent). |
-Unless the control with the name <code>"1"</code> also |
-happened to be the control with the index 1. This problem would also |
-apply to <code>FORM</code> elements within the <code>document.forms</code> |
-collection. |
-</p> |
- |
-<p id="faComMis_4"> |
-Generally it is best to only give form controls names that cannot |
-conflict with existing properties of <code>FORM</code> elements |
-and <code>FORM</code> elements names that cannot conflict with |
-existing properties of the <code>document.forms</code> collection |
-or the <code>document</code> object. |
-</p> |
- |
-<h2 id="faAnon">Anonymous Form References</h2> |
- |
-<p id="faAnon_1"> |
-Because <code>FORM</code> elements are available as integer indexed |
-member of the <code>document.forms</code> collection it is not |
-necessary to know the name of a form (or for the form to have a |
-name) to acquire a reference to it. While being able to refer to a |
-form anonymously with its index might seem like a good approach |
-towards making more general/flexible functions for form validation |
-and the like, in practice referring to forms by their index actually |
-makes code less flexible and harder to maintain. As soon as the |
-number or layout of forms on a page is changed their indexes also |
-change, requiring that all of the references by index be located |
-and altered. |
-</p> |
- |
-<p id="faAnon_2"> |
-One method of avoiding having to know the name of a <code>FORM</code> |
-element within a function that is to act upon a form is to pass a |
-reference to the form object as an argument in the function call. |
-This is easiest achieved from the code provided as the value for |
-an event handling attribute because that code is used by the browser |
-to create an event handling function that is assigned as a method of |
-the element to which it is attached. And in any function executed as |
-a method of an object the <code>this</code> keyword is a reference to |
-the object with which the execution of the method is associated. The |
-most common need to anonymously pass a reference to a <code>FORM</code> |
-element is as an argument to a form validation function in the |
-onsubmit handler of the form. In that case the event handling function |
-is a method of the <code>FORM</code> element so the <code>this</code> |
-keyword refers to the form directly:- |
-</p> |
- |
-<pre id="faAnon_ex1"> |
-function validateForm(formRef){ |
- <span class="commentJS">/* Use the reference to the form passed as the formal |
- parameter - formRef - to acquire a reference to the form |
- control with the name "textField": |
- */</span> |
- var el = formRef && formRef.elements["textField"]; |
- <span class="commentJS">/* If the control reference exists return its value property |
- converted to a boolean value (false if empty, true otherwise) |
- else return true so the form is submitted and the server can |
- do the validation: |
- */</span> |
- return !el || Boolean(el.value); |
-} |
-... |
-<form action="http://example.com/somePage.asp" |
- onsubmit="return validateForm(this);"> |
- <input type="text" name="textField" value=""> |
- <input type="submit" name="Submit_Button" value="Submit"> |
-</form> |
-</pre> |
- |
-<p id="faAnon_3"> |
-Because the above function receives the reference to the form that it |
-is to validate as an argument when it is called it can also be used |
-with any number of other forms. Although in the case of the above |
-function each of those forms would need to contain a field called |
-<code>"textField"</code>, but that string name could also be |
-passed as an argument, making the function more general. |
-</p> |
- |
-<p id="faAnon_4"> |
-Form control objects all have a property named |
-<code>"form"</code> that holds a reference to the |
-<code>FORM</code> element that contains them. As a result the event |
-handling functions attached to form controls can pass an anonymous |
-reference to the form that contains them as <code>this.form</code>. |
-Obviously a function called from an event handler on a control can |
-be passes an anonymous reference to the control itself as |
-<code>this</code>. |
-</p> |
- |
-<h2 id="faBut">Radio Button and Other Control Collections</h2> |
- |
-<p id="faBut_1"> |
-Radio button controls work to provide a selection of one item of many |
-when each of the radio button alternatives has the same |
-<code>NAME</code> attribute. But it is also possible to give other types |
-of control the same <code>NAME</code> attribute. |
-</p> |
- |
-<p id="faBut_2"> |
-When controls that share the same <code>NAME</code> attribute they can |
-still be accessed as integer indexed members of the <code>FORM</code> |
-element's <code>elements</code> collection but when the member of the |
-<code>elements</code> collection is accessed using the <code>NAME</code> |
-attribute value as a property name browsers return a collection all of |
-the elements with the corresponding NAME attributes. So given the <span class="initialism" title="HyperText Mark-up Language"><abbr title="HyperText Mark-up Language">HTML</abbr></span>:- |
-</p> |
- |
-<pre id="faBut_ex1"> |
-<form name="testForm" action="http://example.com/somePage.jsp"> |
- <ul style="list-style-type:none;"> |
- <li><input type="radio" name="radioSet" value="R1">option 1</li> |
- <li><input type="radio" name="radioSet" value="R2">option 2</li> |
- <li><input type="radio" name="radioSet" value="R3">option 3</li> |
- <li><input type="radio" name="radioSet" value="R4">option 4</li> |
- </ul> |
- <input type="submit" name="Submit_Button" value="Send"> |
-</form> |
-</pre> |
- |
-<p id="faBut_3"> |
-A property accessor referring to the member of the <code>FORM</code>'s |
-<code>elements</code> collection by the property name |
-<code>"radioSet"</code> will not return a reference to any one |
-of the named radio buttons but instead returns a collection of all of the |
-like-named radio controls. |
-</p> |
- |
-<p id="faBut_4"> |
-The individual radio buttons within that collection are referred to as |
-integer indexed members of that collection. So to find the button that |
-is checked one might loop through all of the members of the collection |
-of like-named radio buttons and copy a reference to the button with its |
-<code>checked</code> property set to boolean <code>true</code>. |
-</p> |
- |
- |
-<pre id="faBut_ex2"> |
-var radioCollection, checkedButton; |
-var frm = document.forms["testForm"]; |
-if(frm){ |
- radioCollection = frm.elements["radioSet"]; |
- if(radioCollection){ |
- <span class="commentJS">/* The collection of like-named radio buttons has a length |
- property and that is used to limit a for loop:- |
- */</span> |
- for(var c = 0;c < radioCollection.length;c++){ |
- <span class="commentJS">/* The individual radio buttons are accessed as indexed |
- members of the collection using the loop counter - c |
- - from the for loop: |
- */</span> |
- if(radioCollection[c].checked){ |
- <span class="commentJS">/* When a radio button element is found with its |
- checked property set to boolean true a reference |
- to that element is assigned to the local variable |
- - checkedButton - and the loop is terminated with |
- the - break - statement as only one button in a set |
- of like-named radio buttons will be checked at a |
- time, so any remaining buttons in the collection |
- must have checked properties set to false: |
- */</span> |
- checkedButton = radioCollection[c]; |
- break; |
- } |
- } |
- if(checkedButton){ |
- <span class="commentJS">/* Do something with the reference to the checked radio |
- button (if any). |
- */</span> |
- ... |
- } |
- } |
-} |
-</pre> |
- |
-<p id="faBut_5"> |
-It is not unusual when a form is generated by a server-side script that |
-some forms may have one or more like-named controls. If there is only |
-one control inserted in the form then accessing a member of the |
-<code>elements</code> collection with its name will return a reference |
-to that one control, but if there are multiple elements the returned |
-reference will be to a collection of such controls. While it may be |
-possible to branch client-side code that wants to interact with those |
-controls to handle the two alternatives it adds an unnecessary |
-maintenance burden to do so. |
-</p> |
- |
-<p id="faBut_6"> |
-As handling a returned collection usually involves looping through that |
-collection the simplest way of implementing the client-side code to |
-deal with both collections and individual controls being returned by |
-named properties of the <code>elements</code> collection is to |
-normalise the references to individual controls so that they can be |
-handled as if they were a collection. Looping through a collection |
-involves using the <code>length</code> property of the collection to |
-limit the loop statement and accessing the controls within the |
-collection by integer index. This is exactly the way in which code |
-would loop through the elements of an <code>Array</code>. To allow a |
-script to handle both possibilities with the same code the return of |
-a reference to an individual control could be detected and then that |
-reference used to create a one-element <code>Array</code>. Subsequent |
-code would then treat the <code>Array</code> as if it was a collection and |
-loop through it, but as there is only one element the loop body would |
-only be executed once. |
-</p> |
- |
-<pre id="faBut_ex3"> |
-var radioCollection, checkedButton; |
-var frm = document.forms["testForm"]; |
-if(frm){ |
- radioCollection = frm.elements["radioSet"]; |
- if(radioCollection){ |
- <span class="commentJS">/* But the returned reference might not be a collection in this |
- case. Instead it may be a reference to just one control if |
- there is only one in this form with the name "radioSet". |
- If it is a reference to an individual control it is going to |
- be necessary to normalise it. Because radio button controls |
- do not have - length - properties and collections do that is |
- the property that is going to be tested: |
- */</span> |
- if(typeof radioCollection.length != "number"){ |
- <span class="commentJS">/* The length property is not a number so this cannot be a |
- collection and must be normalised so that the following |
- loop statement can handle it correctly. Normalisation is |
- done by making a reference to the control currently |
- referred to by the - radioCollection - local variable |
- into the first (and only) element of a new Array object |
- and then assigning a reference to that array to the - |
- radioCollection - local variable: |
- */</span> |
- radioCollection = [radioCollection]; |
- } |
- <span class="commentJS">/* The execution of the body of the - for - loop is limited by |
- the - length - property of the collection/Array. |
- */</span> |
- for(var c = 0;c < radioCollection.length;c++){ |
- <span class="commentJS">/* The individual radio buttons are accessed as indexed |
- members of the collection/Array using the loop counter |
- - c - from the for loop: |
- */</span> |
- if(radioCollection[c].cheked){ |
- checkedButton = radioCollection[c]; |
- break; |
- } |
- } |
- if(checkedButton){ |
- <span class="commentJS">/* do something with the reference to the checked radio |
- button (if any). |
- */</span> |
- ... |
- } |
- } |
-} |
-</pre> |
- |
-<p id="faBut_7"> |
-While it is normal for radio button controls to be like-named it is |
-also possible for all other controls to be included in a form with |
-multiple controls of the same type and like names. The same |
-referencing techniques can be used with any type of control (even |
-mixed types with like-names, though that is almost never done). But |
-deciding whether a reference needs to be normalised by making it into |
-the only element of an <code>Array</code> by testing the |
-<code>length</code> property of that reference to see if it doesn't |
-exist will not work with <code>SELECT</code> elements as they have |
-a <code>length</code> property of their own. It also would not help |
-to be testing some other characteristic of a collection, such as their |
-<code>item</code> method, as <code>SELECT</code> elements usually have |
-all of the methods and properties of a collection as they are |
-implemented as collections of <code>OPTION</code> elements. |
-</p> |
- |
-<p id="faBut_8"> |
-Turning the problem around and testing a returned reference to see if |
-it has the characteristics of a <code>SELECT</code> element (such as |
-an <code>options</code> property) would be better but some collection |
-implementations have all of the properties and methods of the first |
-element within that collection as well as the properties and methods |
-of a collection (e.g. on IceBrowser 5). That means that it would not be |
-easy to distinguish a collection of <code>SELECT</code> controls |
-from an individual <code>SELECT</code> control. However, a more |
-elaborate test might go:- |
-</p> |
- |
-<pre id="faBut_ex4"> |
-<span class="commentJS">/* Normalise a reference that may be an individual from control |
- (including SELECT elements) or may be a collection of controls |
- into a form that can be handled in a - for - loop controlled with |
- its - length - property and accessed by integer index. |
-*/</span> |
-if((typeof contrlCollection.length != "number")|| <span class="commentJS">//no length propety:</span> |
- <span class="commentJS">/* or:- */</span> |
- ((contrlCollection.options)&& <span class="commentJS">//it has an options colleciton and:</span> |
- ((!contrlCollection[0])|| <span class="commentJS">//no object at index 0 - not a collection</span> |
- <span class="commentJS">/* or:- */</span> |
- (contrlCollection[0] == contrlCollection.options[0])))){ |
- <span class="commentJS">/* The object at index 0 in contrlCollection is the same object |
- as is at index 0 in contrlCollection.options so this must be |
- an individual SELECT element not a collection of them because a |
- collection of SELECT elements would not have an OPTION element |
- at index zero. |
- */</span> |
- contrlCollection = [contrlCollection]; |
-} |
-</pre> |
- |
-<h2 id="faEff">Efficient use of Form Accessors</h2> |
- |
-<p id="faEff_1"> |
-Code that interacts with <code>FORM</code> elements and controls |
-through the <code>document.forms</code> collection and the form's |
-<code>elements</code> collection usually does not do enough work to |
-make the efficiency of the code significant. But with large forms with |
-many controls an inefficiently coded validation function (or some other |
-interaction, like keeping running totals) can negatively impact on the |
-user's experience. It can also be argued that considering the |
-efficiency of implementation is a worthwhile habit even when it would |
-make no perceivable difference. |
-</p> |
- |
-<p id="faEff_2"> |
-A significant aspect of the efficient coding of form interacting code |
-is the re-resolving of references to various objects. This trivial |
-example code copies the values of 5 <code>INPUT</code> elements to |
-local variables:- |
-</p> |
- |
-<pre id="faEff_ex1"> |
-var txt1 = document.forms["formName"].elements["field1"].value; |
-var txt2 = document.forms["formName"].elements["field2"].value; |
-var txt3 = document.forms["formName"].elements["field3"].value; |
-var txt4 = document.forms["formName"].elements["field4"].value; |
-var txt5 = document.forms["formName"].elements["field5"].value; |
-</pre> |
- |
-<p id="faEff_3"> |
-The shortcut accessors require the resolution of fewer object |
-references:- |
-</p> |
- |
-<pre id="faEff_ex2"> |
-var txt1 = document["formName"]["field1"].value; |
-var txt2 = document["formName"]["field2"].value; |
-var txt3 = document["formName"]["field3"].value; |
-var txt4 = document["formName"]["field4"].value; |
-var txt5 = document["formName"]["field5"].value; |
-</pre> |
- |
-<p id="faEff_4"> |
-But their use still involves re-resolving the form object each time a |
-form control is accessed. It would be unnecessary to re-resolve this |
-reference if a reference to the form was assigned to a local variable:- |
-</p> |
- |
-<pre id="faEff_ex3"> |
-<span class="commentJS">/* Assign a reference to the form object to the local variable - frm - |
- and then make subsequent control references relative to that local |
- variable: |
-*/</span> |
-var frm = document.forms["formName"]; |
-var txt1 = frm.elements["field1"].value; |
-var txt2 = frm.elements["field2"].value; |
-var txt3 = frm.elements["field3"].value; |
-var txt4 = frm.elements["field4"].value; |
-var txt5 = frm.elements["field5"].value; |
-</pre> |
- |
-<p id="faEff_5"> |
-The effect would be much the same as when a reference to the form |
-object has been passed to a function and control references are |
-accessed relative to the parameter holding the form reference. |
-</p> |
- |
-<p id="faEff_6"> |
-It is still not optimum to be re-resolving the <code>elements</code> |
-collection, and it is practical to assign a reference to that object |
-to a local variable instead of a reference to the form object:- |
-</p> |
- |
-<pre id="faEff_ex4"> |
-<span class="commentJS">/* Assign a reference to the form's elements collection to the local |
- variable - frmEls - and then make subsequent control references |
- relative to that local variable: |
-*/</span> |
-var frmEls = document.forms["formName"].elements; |
-var txt1 = frmEls["field1"].value; |
-var txt2 = frmEls["field2"].value; |
-var txt3 = frmEls["field3"].value; |
-var txt4 = frmEls["field4"].value; |
-var txt5 = frmEls["field5"].value; |
-</pre> |
- |
-<p id="faEff_7"> |
-With the original long form accessor the resolution starts with |
-resolving the <code>"document"</code> identifier. The |
-identifier is first looked for among the local variables of the |
-function (as a named property of the internal |
-"Variable" object, by ECMA specification), |
-when it is not found the scope chain is searched, object by object |
-down the chain, for a property with the corresponding name. When the |
-scope resolution for <code>"document"</code> gets to the |
-global object (at the end of the scope chain) it will find a property |
-called <code>"document"</code>, a reference to the |
-<code>document</code> object, and the first identifier in the accessor |
-will have been resolved. The next identifier is |
-<code>"forms"</code> and that is located as a property of the |
-<code>document</code>. Then the <code>"formName"</code> |
-property is identified in the <code>forms</code> collection. Next the |
-<code>"elements"</code> property of the form is located, |
-followed by the control name in that object and finally the |
-<code>"value"</code> property of the control is returned. |
-</p> |
- |
-<p id="faEff_8"> |
-When a reference to the <code>elements</code> collection is assigned to |
-a local variable the first identifier in the property accessor is |
-identified as that local variable, the control name is identified as a |
-property of the <code>elements</code> collection referenced and the |
-<code>"value"</code> property of the control is returned. |
-</p> |
- |
-<p id="faEff_9"> |
-Obviously there is a big difference in the amount of work involved in |
-acquiring the <code>value</code> of the control in each case. That |
-difference will not be that significant if only a couple of values are |
-accessed, but even with as few as half a dozen control property |
-accesses the second approach will obviously be much more efficient, |
-and with increasing numbers of controls the difference could easily |
-become apparent to the user. |
-</p> |
-</body> |
-</html> |