Re: javascript custom object event handling and this

Tech-Archive recommends: Repair Windows Errors & Optimize Windows Performance

From: Chris Barber (chris_at_blue-canoe.co.uk.NOSPAM)
Date: 10/13/04


Date: Wed, 13 Oct 2004 02:49:20 +0100

Oh heck, I knew I hated JavaScript but didn't realise how much. Sorry if I got this the wrong way
round, it's been a while since I last tried to get it straight and I thought I could see what was
going on for Jonathan based on some aspects of the this keyword that I'd experienced some time ago
when using:

object.event = handlerfunction;

as opposed to:

<HTMLTAG event="handlerfunction();"/>

Cheers.

Chris.

"Richard Cornford" <Richard@litotes.demon.co.uk> wrote in message
news:ckbm0u$i2g$1$8302bc10@news.demon.co.uk...
Chris Barber wrote:
> The method you are using to assign the event handler actually
> creates a copy of the event handler function and thus the
> this keyword is now referring to the object that called the
> event handler.

That isn't true. The initial act of assigning a reference to a function
object (resulting from the evaluation of a function expression) to a
property of the object's prototype is the only point at which a new
function object is created. The later act of assigning the value of
the - HandleKeyDown - property of an object instance to the event
handling property of the document does exactly, and only, that. i.e.:
The value of instance's - HandleKeyDown - is a reference to a function
object and it is that reference that is assigned to the event handling
property of the document, both properties then refer to the same (and
only) function object.

The value associated with the - this - keyword when that function object
is executed is determined by how the function is called. If it is called
as a method of an object (any object) then - this - refers to that
object. If it were not called as a method of an object (but directly
executed) then - this - would refer to the global object.

All javascript functions are function objects and exhibit the same
behaviour with regard to the - this - keyword regardless of how and
where they were created.

The problem is that once the reference to the function object has been
assigned to the even handling property of the document, when the event
handler is executed it is called as a method of the document and -
this - is thus a reference to the document.

> You can do this:
>
> <begin code>
>
> //object
> function MyObj()
> {
> this.Prop = "MyObj.Prop";
> }
>
> MyObj.prototype.HandleKeyDown = function()
> {
> // if left arrow key is pressed, alert MyObj.Prop
> if (event.keyCode == 37) alert(this.Prop);
> }
>
> var o = new MyObj();
>
> window.document.onkeydown =
> New Function("return o.HandleKeyDown();")
<snip>

That unambiguously is creating a new function object. And it assumes
that - o - is a stable global variable to which a reference to an
instance of the right type of object has been assigned. It also places
the code that creates the global reference, and assigns the event
handler, outside of the object.

That isn't a problem for a small-ish page specific script, and this
looks like a smallish page specific script because attempting to use an
instance method of the - MyObject - object as the onkeydown event
handler suggests that no more than one instance of - MyObject - will
ever be used (as there is only one - document.onkeydown - handler
available).

In a more general implementation, with the intention of creating more OO
code that was easily re-usable, it is common to need to be in a position
to call an object instance method (often with an event handler) without
any regard for the actions of code that is external to the object. Thus
the object needs to be able to refer to its own instance in a way that
is anonymous (or apparently anonymous to external code). Usually with
the code that does the assigning of the event handler moved inboard (and
a reference to the object to which the handler is to be attached passed
in as a parameter to the constructor or a method call).

A number of techniques (and variations) can be used to associate an
object instance with an event handler anonymously of external code. One
of the simplest is for the constructor to independently arrange a global
reference to its object instances and store sufficient information in
each object for it to be able to resolve a reference to itself
globally:-

Record the current length of the globally accessible array of instances
as the - this.index - property of the constructed object (so that this
object instance can look itself up in the array) and assign a reference
to the constructed object to the array element at that index:-

function MyObj(){
    /* Record the current length of the globally accessible array
       of instances as the - this.index - property of the
       constructed object (so that this object instance can look
       itself up in the array) and assign a reference to the
       constructed object to the array element at that index:-
    */
    MyObj.instances[(this.index = MyObj.instances.lenght)] = this;
    this.Prop = "MyObj.Prop";
}
/* Create an array that can store individual object
   instance references:-
*/
MyObj.instances = [];

MyObj.prototype.HandleKeyDown = function(e){
    e = e||window.event;
    // if left arrow key is pressed, alert MyObj.Prop
    if (e.keyCode == 37) alert(this.Prop);
}

This approach lends itself to the use of the Function constructor to
create the actual event handler, and to objects that want to use
document.write to insert HTML source that refers back to the individual
object instance. For example:-

MyObj.prototype.attachDocKeyHandler = function(doc){
    /* Create a new event handling function that calls an instance
       method of this object without any regard for how external
       code is using this object instance.
    */
    doc.onkeydown = new Function(
        'e',
        ('MyObj.instances['+this.index+'].HandleKeyDown(e)')
    );
}

- or:-

MyObj.prototype.writeInputField = function(){
    /* Write an input field that has a onkeydown handler that calls
       a method of this object instance.
    */
    document.write(
        ('<input type="text" value="" onkeydonw="'+
        'MyObj.instances['+this.index+'].HandleKeyDown(event)'+
        '">')
    );
}

Most other techniques for associating object instances with event
handlers are closure-based, but they tend to be truly anonymous, and
lend themselves to circumstances where constructing string references
would be inconvenient (such as when creating DOM elements) and direct
object and function references more useful. Closures are big subject and
allow many variations on specific techniques. A specific example, and an
explanation of closures can be found at:-

<URL: http://jibbering.com/faq/faq_notes/closures.html >

Richard.

---
Outgoing mail is certified Virus Free.
Checked by AVG anti-virus system (http://www.grisoft.com).
Version: 6.0.776 / Virus Database: 523 - Release Date: 12/10/2004


Relevant Pages

  • Re: javascript custom object event handling and this
    ... >> The method you are using to assign the event handler actually ... The initial act of assigning a reference to a function ... > object instance with an event handler anonymously of external code. ...
    (microsoft.public.scripting.jscript)
  • Re: javascript custom object event handling and this
    ... > The method you are using to assign the event handler actually ... The value of instance's - HandleKeyDown - is a reference to a function ... to call an object instance method without ...
    (microsoft.public.scripting.jscript)
  • RE: AddHandler
    ... long as you need your event handler to run. ... If you only use a local variable to hold a reference to the text box, ... handler will stop to work after the garbage collector sweeps it away. ... Private Sub ThisDocument_OpenHandles ThisDocument.Open ...
    (microsoft.public.vsnet.vstools.office)
  • Re: Strange issue in Excel addin
    ... I store the reference to Excel application in a global variable that is ... when I access it from within a specific event handler. ...
    (microsoft.public.office.developer.com.add_ins)
  • Trouble with event
    ... public event Evento Modified; ... Every time I create an instance, I say it witch is the handler of ... there is a reference to an object not established as an object instance. ...
    (microsoft.public.dotnet.languages.csharp)