Re: javascript custom object event handling and this

From: Richard Cornford (Richard_at_litotes.demon.co.uk)
Date: 10/10/04

  • Next message: Don: "Communicating to server .php without using <form>"
    Date: Sun, 10 Oct 2004 16:55:09 +0100
    
    

    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.


  • Next message: Don: "Communicating to server .php without using <form>"

    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)

    Loading