Jonathan Fine avatar Jonathan Fine committed fba54e5

$ hg cp source/counters-discuss.rst source/counters-memory.rst

Comments (0)

Files changed (2)

source/counters-memory.rst

+Counters discussion
+===================
+
+Here we go though the file *counter.js* broken into small pieces,
+explaining what's going on.
+
+Namespace
+---------
+
+We define and execute an anonymous function in order to create a
+private namespace.  Just like any other function, variables declared
+and used in the function are not accessible from outside, unless we
+explicitly provide access.  Returning a value is one way to do this.
+Adding an attribute to the global object, or something accessible from
+the global object, is another.
+
+.. literalinclude:: /examples/counters.js
+   :language: javascript
+   :lines: 1-3
+
+This use of *function* to provide a namespace is similar in some ways to
+the module concept in Python.  Some people call this use of *function*
+in JavaScript the *module pattern*.
+
+Create
+------
+
+From one point of view, the *create* function should be, but is not, a
+built-in function in JavaScript.  Instead we have to create it
+ourselves out of the *new* function, which is but should not be part
+of JavaScript.
+
+That point of view is that JavaScript's prototype inheritance is best
+understood for what it is, rather than presented as if *new* in
+JavaScript is similar to Java's *new*.  That point of view also say's
+that code should be written to use *create* when required, but that
+use of *new* is considered harmful (except to define *create*, as
+below).
+
+.. literalinclude:: /examples/counters.js
+   :language: javascript
+   :lines: 4-11
+
+The *create* function creates a new object whose parent in the
+inheritance tree is the *parent* argument.  What could be simpler than
+that?  The *create* function is similar to Python's *__new__*, in that
+it gives the class/parent of an object.
+
+The *new* operator has semantics such that the above code provides an
+implementation of *create*.  However, we don't for now need the
+somewhat complex semantics of *new*.
+
+Global object
+-------------
+
+JavaScript has a global object, whereas Python does not. (Python's
+main module is similar but in many important ways different.)
+JavaScript's global object can be the cause of many obscure and
+hard-to-diagnose problems, and so it's generally best to take care
+when using it.
+
+Therefore, it is best to make explicit any use of the global object,
+and a good way to do this is to introduce a variable, called *global*
+of course, whose value is the global object.  That way, when you
+access the global object or its properties, you can see that's what
+you're doing.
+
+.. literalinclude:: /examples/counters.js
+   :language: javascript
+   :lines: 12-13
+
+This line of code executes an anonymous function which returns the
+value of *this* during the execution of the anonymous function.  Due
+to the semantics of JavaScript and the way the function is called, in
+the anonymous function *this* is the global object.  Thus, the
+anonymous function returns the global object, which we store in the
+*global* variable (which confusingly is local to the function).
+
+In some situations, such as above, the global object is available as
+the value of *this*, but sometimes *this* refers to something else.
+So using *this* to refer to the global object is not a good idea.
+
+In browsers the global object has an attribute called *window* whose
+value is the global object.  It is as if we had written
+
+   .. code-block:: javascript
+   
+        global.window = global;
+
+It is better to write *window* rather than *this*, but writing
+*global* for the global object is best of all.  (Command line
+JavaScript interpreters don't start with *window* as the global
+object, and in the browser *window* has many special properties.)
+
+Counter class
+-------------
+
+Every class needs a prototype object.  We'll call it *counter*.  Don't
+confuse the *counter* prototype with a *Counter* instance, which might
+also be called *counter*.  Fortunately, in well organised code the
+prototype object of a class is in one namespace/module, and instances
+are in different namespaces.
+
+Here's the implementation of *Counter*.  It relies on a function
+*__init__*, similar to Python's *__init__*, which we haven't defined
+yet.
+
+.. literalinclude:: /examples/counters.js
+   :language: javascript
+   :lines: 14-22
+
+A word about the use of *apply*.  In Python we would write something a
+bit like
+
+   .. code-block:: python
+   
+        def Counter(*args, **kwargs):
+
+             instance = object.__new__(counter)
+             instance.__init__(*args, **kwargs)
+             return instance
+
+In JavaScript we don't have keyword arguments, and instead of *args*
+we have a keyword *arguments* which has special properties.  These
+properties, together with those of *apply*, cause the JavaScript code
+above to have the same general effect as the Python code.
+
+(We won't sweat the details now.  Most of the time use of *call*,
+*apply*, *create* and *arguments* can and should be hidden behind the
+scenes.  But you need to know that this can be done so that when the
+time come, **you** can provide an efficient and elegant interface by
+using them to refactor complexity into something that is put behind
+the scenes.)
+
+The gist of the above code is that
+
+   .. code-block:: python
+
+      var my_counter = Counter(arg1, ...)
+
+causes *my_counter* to be an object, whose parent is the *counter*
+prototype, and which has been initialised by the *__init__* function.
+
+
+Counter properties
+------------------
+
+The *__init__* method is called, of course, when a newly created child
+of the *counter* prototype needs to be initialised.  Each counter has
+a *name*, the thing being counted, and its *count*.
+
+.. literalinclude:: /examples/counters.js
+   :language: javascript
+   :lines: 23-28
+
+When a counter is clicked, its count is increased by one.  We'll come
+to the display of counters next.
+
+.. literalinclude:: /examples/counters.js
+   :language: javascript
+   :lines: 29-33
+
+Often, the most efficient way of changing the content of a DOM node is
+to use its *innerHTML* property.  This method works well with
+delegation, as with delegation we don't have to add (or remove)
+handlers from nodes created (or destroyed) in this way.
+
+This implementation is very simple.  In a production system *name*
+would be escaped.  The argument *parent_id* is provided in case the
+*html* method wants to create IDs for its subnodes.  This would
+happen, for example, if the returned HTML was for a slideshow with its
+controls.
+
+.. literalinclude:: /examples/counters.js
+   :language: javascript
+   :lines: 34-38
+
+
+Onload
+------
+
+This code is specific to a particular page or perhaps group of pages.
+It creates *Counter* instances (sensible, as we want to count) and
+sets up links between the DOM and the JavaScript.
+
+We can't change the DOM until the nodes we wish to change have been
+constructed.  In browsers the global object has an *onload* event that
+can be used to solve this timing problem.
+
+Here, when the page loads we create an array of counters.
+
+.. literalinclude:: /examples/counters.js
+   :language: javascript
+   :lines: 39-47
+
+We also need to link the counters, which are JavaScript objects, to
+DOM nodes and events.  Let's suppose we're to display the counters in
+an element whose ID is *example*.
+
+This will do the job.  Notice the inelegant way in which we initialise
+the display of the counters.  Clearly this won't do if we're inserting
+say a slideshow into the page.  It's an exercise to write something
+better.
+
+.. literalinclude:: /examples/counters.js
+   :language: javascript
+   :lines: 48-56
+
+We now need to link DOM events to the counters we created.  The
+counters are stored in the *models* variable (which is local to the
+onload function).  Each counter element on the DOM corresponds, via
+its ID, to a counter instance in the models array.  This was done on
+purpose.
+
+When a counter element on the page is clicked we can, from its ID,
+find the correspond counter instance in the models and, so to speak,
+click it.  We can also ask the counter instance to generate new HTML
+for the refreshing of the counter element.
+
+In short, everything has been set up to make delegation as easy as
+possible.  We'll assume that the generic function *onclick_factory*
+will handle the delegation (and cross browser issues) for us.
+
+.. literalinclude:: /examples/counters.js
+   :language: javascript
+   :lines: 57
+
+This line of code helps prevent a memory leak in Internet Explorer,
+prior to IE8.
+
+.. literalinclude:: /examples/counters.js
+   :language: javascript
+   :lines: 58
+
+And finally we can finish the onload function.
+
+.. literalinclude:: /examples/counters.js
+   :language: javascript
+   :lines: 59-60
+
+
+Delegation
+----------
+
+In an ideal production environment this code would come from a
+well-supported standard library.  However, we're not there yet.
+
+The *onclick_factory* is an example of a closure.  The returned
+function has a hidden reference to the models argument that is passed
+to the *onclick_factory*.  Each execution of *onclick_factory* refers
+to the *models* argument that was passed to it.
+
+.. literalinclude:: /examples/counters.js
+   :language: javascript
+   :lines: 61-79
+
+Closing namespace
+-----------------
+
+.. literalinclude:: /examples/counters.js
+   :language: javascript
+   :lines: 80-
+
+This little piece of line noise does three things.  
+
+1. It uses **}** to close the function definition.  
+
+2. It then uses **)** to close the function expresssion.
+
+3. Finally, it uses **()** to execute the function.
+
+The final semi-colon **;** closes the statement.  (It's generally best
+when writing code to put in the semicolons yourself, rather then let
+JavaScript put them in.)
+
+
+Memory
+------
+
+Understanding garbage collection and memory leaks is an advanced
+topic, but even beginners should know that there can be problems here.
+Correct use of a suitable framework is the simplest way to avoid
+memory leaks.
+
+The code in the above example indicates how to construct and use such
+a framework.
+
+Garbage collection
+------------------
+The JavaScript garbage collection deletes objects provide it can
+discover that they can never be used again.  The simplest case is:
+
+   .. code-block:: javascript
+
+      x = [1, 2, 3, 4, 5, 6, 7];
+      x = null;
+
+The second assignment to *x* ensures that the original array (created
+by the array literal) can no longer be accessed, and so it can be
+garbage collected.
+
+After the namespace anonyomous function has executed there are no
+global objects holding even indirect references to the function.
+Therefore is can and will be garbage collected. However, as we shall
+see, the execution context of the function continues.
+
+IE memory leaks
+---------------
+
+Internet Explorer, prior to IE8 (check) had **two memory heaps**, one
+for JavaScript and the other for the DOM.  Each heap had its own
+garbage collector.  
+
+This means that if a DOM node *d* held a reference to a JavaScript
+object *j* and also that *j* held a reference to *d* then neither
+garbage could collect *j* or *d*.  What's worse, even when the page
+was unloaded IE did not reclaim this memory.
+
+Thus, prior to IE8, JavaScript could cause Internet Explorer to leak
+memory.  This lost memory could be reclaimed only by closing the
+browser!
+
+Residue
+-------
+
+The *example* DOM node continues to exist, as it can be reached from
+the *document* node.  It has an *onclick* attribute, which is the
+function created by the *onclick_factory*, with *models* as the
+argument.  So that function is not garbage collected.
+
+The function *example.onclick* retains a reference to *models* just as
+surely as executing
+
+   .. code-block:: javascript
+
+      var f = function(arg){ return [1, 2, 3, arg] };
+      var y = f(x);
+
+causes *y* to retain a reference to the value of *x*.
+
+Finally, *models* contains references to the *Counter* instances and
+thus, by the hidden prototype reference, to the *counter* prototype
+object.
+
+The *counter* prototype object lies in the execution context of the
+anonymous function, and as it happend that keeps alive the whole of
+the execution context.
+
+Reclaiming memory
+-----------------
+
+Notice that the JavaScript holds only two reference to DOM nodes,
+namely *document* and *example*.  However, the line of code
+
+   .. code-block:: javascript
+
+      example = undefined;    // Release reference to DOM node.
+
+means that the JavaScript does not hold a reference to *example*.
+
+The DOM node *example* holds a reference, via its *onclick* function,
+to the JavaScript on the page, and in particular to the *Counter*
+instances.  This cannot be avoided, because we want certain DOM nodes
+to change the state of the counters.
+
+However, the converse is not true.  We can write the JavaScript code
+so that it has but one reference to a DOM object, namely the
+*document* node held for example as a property of the *global* object.
 
    counters-example
    counters-discuss
+   counters-memory
 
 Reference:
 
Tip: Filter by directory path e.g. /media app.js to search for public/media/app.js.
Tip: Use camelCasing e.g. ProjME to search for ProjectModifiedEvent.java.
Tip: Filter by extension type e.g. /repo .js to search for all .js files in the /repo directory.
Tip: Separate your search with spaces e.g. /ssh pom.xml to search for src/ssh/pom.xml.
Tip: Use ↑ and ↓ arrow keys to navigate and return to view the file.
Tip: You can also navigate files with Ctrl+j (next) and Ctrl+k (previous) and view the file with Ctrl+o.
Tip: You can also navigate files with Alt+j (next) and Alt+k (previous) and view the file with Alt+o.