setTimeout and an object's methods

Discussion in 'Javascript' started by Andrew Poulos, Mar 5, 2006.

  1. With the following code I can't understand why this.num keeps
    incrementing each time I create a new instance of Foo. For each instance
    I'm expecting this.num to alert as 1 but keeps incrementing.

    Foo = function(type) {
    this.num = 0;
    this.type = type
    this.trigger();
    }
    Foo.prototype.trigger = function() {
    me = this;
    setTimeout("me.test();",1000);
    }
    Foo.prototype.test = function() {
    this.num++;
    alert(this.type + "+" + this.num);
    }

    f1 = new Foo("a");
    f2 = new Foo("b");
    f3 = new Foo("c");

    Another aspect I can't resolve is why the alert of this.type is "c" each
    time. I guess the issues are being caused by the setTimeout. What's
    happening and what's a better way to do this?

    Andrew Poulos
     
    Andrew Poulos, Mar 5, 2006
    #1
    1. Advertising

  2. Andrew Poulos wrote:
    > With the following code I can't understand why this.num
    > keeps incrementing each time I create a new instance of
    > Foo. For each instance I'm expecting this.num to alert
    > as 1 but keeps incrementing.
    >
    > Foo = function(type) {
    > this.num = 0;
    > this.type = type
    > this.trigger();
    > }
    > Foo.prototype.trigger = function() {
    > me = this;

    ^^
    Assigning a value to an Identifier that is not declared in any
    containing scope results in the creation of a new property of the global
    object, the practical equivalent to the runtime creation of a global
    variable. And there is only one property of the global object created
    the first time - me = this; - is executed, subsequent executions of the
    statement do not create the value they just assign that one property of
    the global object a new value.

    > setTimeout("me.test();",1000);

    ^^^^^^^^^^
    The string arguments to - setTimeout - are evaluated after the timeout
    has expired. The - me - identifier will be resolved at that time, to
    whichever was the last value assigned to the - me - property of the
    global object.

    Having - setTimeout - delay for one second will give all three object
    creation statements time to complete and the - me - property of the
    global object will have the value of the - this - reference in the last
    object created. When the three setTimouts happen - me - is the object
    with the - type - of 'c' and its test method will be called 3 times,
    incrementing the - num - property of that single object only.

    > }
    > Foo.prototype.test = function() {
    > this.num++;
    > alert(this.type + "+" + this.num);
    > }
    >
    > f1 = new Foo("a");
    > f2 = new Foo("b");
    > f3 = new Foo("c");


    These three statements will be over in milliseconds so they have all
    executed prior to the first evaluation/execution of a - setTimeout -
    string argument.

    >
    > Another aspect I can't resolve is why the alert of this.type
    > is "c" each time. I guess the issues are being caused by
    > the setTimeout.


    The issue is being caused by trying to use one global property to refer
    to many object instances. That was never going to work.

    > What's happening and what's a better way
    > to do this?


    The 'this' shown here is trivial and pointless, so the best way of doing
    it is to delete the code and forget about it. The 'this' that is your
    real problem context is probably a candidate for three or four possible
    approaches the base of which can only be determined by knowing what the
    real 'this' actually is.

    Richard.
     
    Richard Cornford, Mar 5, 2006
    #2
    1. Advertising


  3. >> Foo = function(type) {
    >> this.num = 0;
    >> this.type = type
    >> this.trigger();
    >> }
    >> Foo.prototype.trigger = function() {
    >> me = this;

    > ^^
    > Assigning a value to an Identifier that is not declared in any
    > containing scope results in the creation of a new property of the global
    > object,



    >> What's happening and what's a better way
    >> to do this?

    >
    > The 'this' shown here is trivial and pointless, so the best way of doing
    > it is to delete the code and forget about it. The 'this' that is your
    > real problem context is probably a candidate for three or four possible
    > approaches the base of which can only be determined by knowing what the
    > real 'this' actually is.


    Thanks for the clear explanation.

    My new problem is, what the appropriate way a method be called from
    within another method after a defined period of time?

    Andrew Poulos
     
    Andrew Poulos, Mar 5, 2006
    #3
  4. Andrew Poulos wrote:
    <snip>
    >> The 'this' shown here is trivial and pointless, so the best
    >> way of doing it is to delete the code and forget about it.
    >> The 'this' that is your real problem context is probably a
    >> candidate for three or four possible approaches the base of
    >> which can only be determined by knowing what the real 'this'
    >> actually is.

    >
    > Thanks for the clear explanation.
    >
    > My new problem is, what the appropriate way a method be called
    > from within another method after a defined period of time?


    There are very few situations in javascript where a single 'best' or
    'appropriate' way of doing anything can be determined. What should be
    done usually depends a great deal on why you want to do something, and
    the context in which you want to do it. You are the only person in a
    position to provide that information and if you won't you probably won't
    get good advice on what to do. At best you may get the first broadly
    functional approach that someone chooses to suggest, but that may do you
    as much harm as good in the long run.

    Richard.
     
    Richard Cornford, Mar 6, 2006
    #4
  5. Andrew Poulos

    RobG Guest

    Andrew Poulos wrote:
    >
    >>> Foo = function(type) {
    >>> this.num = 0;
    >>> this.type = type
    >>> this.trigger();
    >>> }
    >>> Foo.prototype.trigger = function() {
    >>> me = this;

    >>
    >> ^^
    >> Assigning a value to an Identifier that is not declared in any
    >> containing scope results in the creation of a new property of the global
    >> object,

    >
    >
    >
    >>> What's happening and what's a better way
    >>> to do this?

    >>
    >>
    >> The 'this' shown here is trivial and pointless, so the best way of doing
    >> it is to delete the code and forget about it. The 'this' that is your
    >> real problem context is probably a candidate for three or four possible
    >> approaches the base of which can only be determined by knowing what the
    >> real 'this' actually is.

    >
    >
    > Thanks for the clear explanation.
    >
    > My new problem is, what the appropriate way a method be called from
    > within another method after a defined period of time?


    The 'best' approach will be determined by what you are trying to do.
    Start by calling it the same way you would from a totally separate function.

    Given your Foo constructor:

    var Foo = function(type) {
    this.num = 0;
    this.type = type
    }

    Add a 'trigger' method to the prototype that uses setTimeout to alert
    the value of 'type':

    Foo.prototype.trigger = function() {
    setTimeout('alert("' + this.type + '");',1000);
    }


    'type' is evaluated when the setTimeout object is created, not when
    setTimeout runs. Another way is to use a closure:

    Foo.prototype.trigger = function(type) {
    type = this.type;
    setTimeout(function(){ alert(type); }, 1000);
    }


    Test it:

    var f1 = new Foo('a');
    var f2 = new Foo('b');
    var f3 = new Foo('c');

    // Call trigger() out of order
    f2.trigger(); // Shows 'b'
    f1.trigger(); // Shows 'a'
    f3.trigger(); // Shows 'c'

    f1.type = 'zz';
    f1.trigger(); // Shows 'zz'


    My question here is: it seems that a new function object is created each
    time the trigger() method is called, is that correct? As a result, each
    instance has a local 'type' variable that is given the value of the
    'type' property of the object that called it (i.e. this.type).

    Otherwise all calls to trigger() would show the last value that type was
    set to (i.e. zz since all the code finishes before the first alert appears).

    What is also happening is that a closure is formed by the setTimeout
    objects having a reference to the 'type' property of the trigger
    functions that called them. That may cause memory problems in IE -
    though it may require quite a few calls to be significant.


    Incidentally, the use of 'type' may be confusing as many DOM objects
    have a type property also. You may want to make it 'objType' or
    something more meaningful.

    --
    Rob
     
    RobG, Mar 6, 2006
    #5
  6. RobG wrote:
    <snip>
    > Foo.prototype.trigger = function(type) {
    > type = this.type;
    > setTimeout(function(){ alert(type); }, 1000);
    > }


    If - setTimeout - executes a second later can you be certain that the -
    type - property of the object still has the same value as the - type -
    local variable of the outer function, and will that matter? This is why
    you see such as - var self = this; -, so that the inner function could
    use - self.type - and read the actual value of the pertinent object
    instance property at the time of its execution.

    There is also the question of how often - trigger - will be called. If
    it is once then creating a new closure within the trigger function on
    each execution would not matter. If - trigger - was going to be used
    repeatedly it may be better to only create one closure-forming inner
    function instance, i.e.:-

    function Foo(type) {
    var self = this;
    this.num = 0;
    this.type = type;
    this.trigger = function(){
    alert(self.type);
    };
    }

    >
    > Test it:
    >
    > var f1 = new Foo('a');
    > var f2 = new Foo('b');
    > var f3 = new Foo('c');
    >
    > // Call trigger() out of order
    > f2.trigger(); // Shows 'b'
    > f1.trigger(); // Shows 'a'
    > f3.trigger(); // Shows 'c'
    >
    > f1.type = 'zz';
    > f1.trigger(); // Shows 'zz'
    >
    >
    > My question here is: it seems that a new function object
    > is created each time the trigger() method is called, is
    > that correct?


    Yes it is. ECMA 262 makes provision for function objects to be 'joined'
    when it not possible to distinguish between them but that cannot be true
    here as each new function object has a distinct scope chain assigned to
    its internal [[Scope]] property (each contains a unique
    Activation/Variable object from its outer function's execution context).
    There in also no evidence that any ECMAScript implementations take
    advantage of the specification's provision for joining function objects.

    > As a result, each instance has a local 'type' variable
    > that is given the value of the 'type' property of the
    > object that called it (i.e. this.type).


    In effect, each instance has a unique Activation/Variable object on its
    scope chain and that object has a property named 'type' that corresponds
    with the outer function's local variable and holds the value of that
    variable.

    > Otherwise all calls to trigger() would show the last value
    > that type was set to (i.e. zz since all the code finishes
    > before the first alert appears).
    >
    > What is also happening is that a closure is formed by the
    > setTimeout objects having a reference to the 'type' property
    > of the trigger functions that called them.


    As the - type - property is a primitive value there is no sense in which
    new function can have a reference to it. The object referred to by each
    function instance is the Activation/Variable object.

    > That may cause memory problems in IE -
    > though it may require quite a few calls to be
    > significant.


    No it won't. The memory leak problem is caused by circular references
    (not closures as such) between javascript objects and DOM nodes
    (effectively ActiveX and COM objects). There are no DOM nodes involved
    here.

    Richard.
     
    Richard Cornford, Mar 6, 2006
    #6
  7. Richard Cornford wrote:
    > RobG wrote:
    > <snip>
    >> Foo.prototype.trigger = function(type) {
    >> type = this.type;
    >> setTimeout(function(){ alert(type); }, 1000);
    >> }

    >
    > If - setTimeout - executes a second later can you be certain that the -
    > type - property of the object still has the same value as the - type -
    > local variable of the outer function, and will that matter? This is why
    > you see such as - var self = this; -, so that the inner function could
    > use - self.type - and read the actual value of the pertinent object
    > instance property at the time of its execution.
    >
    > There is also the question of how often - trigger - will be called. If
    > it is once then creating a new closure within the trigger function on
    > each execution would not matter. If - trigger - was going to be used
    > repeatedly it may be better to only create one closure-forming inner
    > function instance, i.e.:-
    >
    > function Foo(type) {
    > var self = this;
    > this.num = 0;
    > this.type = type;
    > this.trigger = function(){
    > alert(self.type);
    > };
    > }
    >
    >> Test it:
    >>
    >> var f1 = new Foo('a');
    >> var f2 = new Foo('b');
    >> var f3 = new Foo('c');
    >>
    >> // Call trigger() out of order
    >> f2.trigger(); // Shows 'b'
    >> f1.trigger(); // Shows 'a'
    >> f3.trigger(); // Shows 'c'
    >>
    >> f1.type = 'zz';
    >> f1.trigger(); // Shows 'zz'
    >>
    >>
    >> My question here is: it seems that a new function object
    >> is created each time the trigger() method is called, is
    >> that correct?

    >
    > Yes it is. ECMA 262 makes provision for function objects to be 'joined'
    > when it not possible to distinguish between them but that cannot be true
    > here as each new function object has a distinct scope chain assigned to
    > its internal [[Scope]] property (each contains a unique
    > Activation/Variable object from its outer function's execution context).
    > There in also no evidence that any ECMAScript implementations take
    > advantage of the specification's provision for joining function objects.
    >
    >> As a result, each instance has a local 'type' variable
    >> that is given the value of the 'type' property of the
    >> object that called it (i.e. this.type).

    >
    > In effect, each instance has a unique Activation/Variable object on its
    > scope chain and that object has a property named 'type' that corresponds
    > with the outer function's local variable and holds the value of that
    > variable.
    >
    >> Otherwise all calls to trigger() would show the last value
    >> that type was set to (i.e. zz since all the code finishes
    >> before the first alert appears).
    >>
    >> What is also happening is that a closure is formed by the
    >> setTimeout objects having a reference to the 'type' property
    >> of the trigger functions that called them.

    >
    > As the - type - property is a primitive value there is no sense in which
    > new function can have a reference to it. The object referred to by each
    > function instance is the Activation/Variable object.
    >
    >> That may cause memory problems in IE -
    >> though it may require quite a few calls to be
    >> significant.

    >
    > No it won't. The memory leak problem is caused by circular references
    > (not closures as such) between javascript objects and DOM nodes
    > (effectively ActiveX and COM objects). There are no DOM nodes involved
    > here.


    Thanks for all the advice (and to RobG).

    I can't tell you precisely what I want to do because my client
    themselves do not know precisely. Basically there are one or more images
    on which "transformations" - combinations of transparency, position,
    source, size... - get (non constant) periodically applied. Some of the
    transformations are, for me anyway, relatively complex and so are
    themselves separate methods.

    On an image I need to apply a series of transformations (randomising the
    delay, length of time to apply, and the actual transformation). So I saw
    it as, once the initial image has loaded, as "jumping from
    transformation method to transformation method.

    There'll be more than one image but less than 12 images to work on.

    Though, for the time being, I don't think I need any more help as I have
    been given a lot to work with already by you (plural).

    Andrew Poulos
     
    Andrew Poulos, Mar 6, 2006
    #7
  8. Andrew Poulos

    VK Guest

    Andrew Poulos wrote:
    > With the following code I can't understand why this.num keeps
    > incrementing each time I create a new instance of Foo. For each instance
    > I'm expecting this.num to alert as 1 but keeps incrementing.
    >
    > Foo = function(type) {
    > this.num = 0;
    > this.type = type
    > this.trigger();
    > }
    > Foo.prototype.trigger = function() {
    > me = this;
    > setTimeout("me.test();",1000);
    > }
    > Foo.prototype.test = function() {
    > this.num++;
    > alert(this.type + "+" + this.num);
    > }
    >
    > f1 = new Foo("a");
    > f2 = new Foo("b");
    > f3 = new Foo("c");
    >
    > Another aspect I can't resolve is why the alert of this.type is "c" each
    > time. I guess the issues are being caused by the setTimeout. What's
    > happening and what's a better way to do this?


    I don't think you will be ever able to reach what you want in the way
    you want. window.setTimeout has been implemented waaay in JavaScript
    1.0 then no one even thought about constructors, prototypes and stuff.
    He runs with this==window and no amount of braces around can change it.
    Any attempts to stick some other "this" are going away like water off a
    duck's back. So if I'm reading your intentions right:

    {
    {
    {
    ... and somewhere deep in the instance curlies
    setTimeout running for an instance method
    ...

    then NOOP - you cannot do it.

    There were some extensive discussion back in 90's (not here), and since
    nothing dramatically changed since then, you may stick to this
    solution.
    You can try to transform setTimeout default into its strength. If it's
    guranteed to run in the global scope, so give him a reference in a
    window property - you can be sure that it will pick it up.

    A sample can be found at
    <http://www.geocities.com/schools_ring/archives/threads.html> and the
    code posted at the end of this message - though I'm affraid newsreader
    may jam everything.

    This sample is a kind of overkill, because I created it while testing
    script engine "thread capacity" plus some dynamic styling discrepancies
    between browsers. Nevertheless the idea is clear (I hope :)

    1) You give an OID (Object ID) to each instance.
    2) On thread start you add new property to the current window where the
    key is instance OID and value is instance reference.
    3) On thread stop you remove this property.
    4) setTimeout is happy to use window[oid].method. setTimeout is happy -
    you are happy :)

    P.S. If you are affraid that it's some kind of "concept profanation"
    ;-) then let me assure you that it's pretty close to what happens in
    "big languages". There an instance also gets an OID and then roaming
    with it like a transit passenger in the LA International, showing his
    ID on each corner. Just in "big languages" it's all done nicely on the
    lower level.

    // Code from
    <http://www.geocities.com/schools_ring/archives/threads.html>

    <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Frameset//EN"
    "http://www.w3.org/TR/html401/frameset.dtd">
    <html>
    <head>
    <title>window.setTimeout</title>
    <meta http-equiv="Content-Type"
    content="text/html; charset=iso-8859-1">
    <script type="text/javascript">

    function Timer(out) {
    // Instance class name:
    this.$name = 'Timer';
    // Instance OID (Object ID):
    this.$oid = this.$name + (new Date()).getTime();
    Timer.$table[this.$oid] = this;
    // Output (DOM element):
    this.$out = out || null;
    // Timer ID:
    this.$tid = null;
    // Minutes counter:
    this.$min = 0;
    // Seconds counter:
    this.$sec = 0;
    // Static methods (shared between instances):
    this.start = Timer.$start;
    this.$tick = Timer.$tick;
    this.stop = Timer.$stop;
    this.toString = Timer.toString;
    }

    Timer.$table = new Object();

    Timer.$start = function() {
    // Start timer instance:
    if (this.$tid == null) {
    var oid = this.$oid;
    var cmd = 'self[\''+oid+'\'].$tick(\''+oid+'\')';
    self[oid] = this;
    this.$tid = window.setTimeout(cmd,1000);
    return this.$oid;
    }
    }

    Timer.$tick = function(oid) {
    // OnTimer instance call:
    with (self[oid]) {
    $sec++;
    if ($sec >= 60) {
    $min++;
    $sec = 0;
    }
    if ($out != null) {
    $out.innerHTML = this.toString($min,$sec);
    }
    var cmd = 'self[\''+oid+'\'].$tick(\''+oid+'\')';
    $tid = window.setTimeout(cmd,1000);
    }
    }

    Timer.$stop = function() {
    // Stop timer instance:
    if (this.$tid != null) {
    window.clearTimeout(this.$tid);
    self[this.$oid] = null;
    this.$tid = null;
    }
    }

    Timer.toString = function(m,s) {
    var t = '';
    if (arguments.length < 2) {
    t = 'function';
    }
    else {
    t = (m < 10)? '0'+m : m;
    t+= ':';
    t+= (s < 10)? '0'+s : s;
    }
    return t;
    }

    Timer.bDown = function(e) {
    // Visual change of pseudo-button on mousedown:
    var evt = e || event;
    if (evt) {
    var trg = evt.target || evt.srcElement;
    trg.style.borderStyle = 'inset';
    }
    }

    Timer.bUp = function(e) {
    // Visual change of pseudo-button on mouseup:
    var evt = e || event;
    if (evt) {
    var trg = evt.target || evt.srcElement;
    trg.style.borderStyle = 'outset';
    }
    }

    Timer.action = function(e) {
    // Display current instance data and options:
    var evt = e || event;
    if (evt) {
    var trg = evt.target || evt.srcElement;
    var oid = trg.title;
    var tmp = Timer.$table[oid];
    var msg = 'Instance OID: ' + oid +'\n\n';
    var val = tmp.toString(tmp.$min,tmp.$sec);
    if (self[oid]) {
    msg+= 'Currently running\n';
    msg+= 'Last value: ' + val + '\n';
    msg+= 'Press Cancel to stop, press OK to keep running';
    if (window.confirm(msg)) {
    tmp.stop();
    tmp.$out.style.color = '#CCCCCC';
    }
    }
    else {
    msg+= 'Currently idle\n';
    msg+= 'Last value: ' + val + '\n';
    msg+= 'Press OK to run, press Cancel to keep idle';
    if (window.confirm(msg)) {
    tmp.start();
    tmp.$out.style.color = '';
    }
    }
    }
    }

    function createTimerBox(i) {
    var t = document.createElement('VAR');
    t.className = 'timer';
    var d = document.createElement('SPAN');
    d.innerHTML = '00:00';
    var b = document.createElement('B');
    b.innerHTML = '?';
    b.title = (new Timer(d)).start();
    b.tabIndex = ++i;
    b.onmousedown = Timer.bDown;
    b.onmouseup = Timer.bUp;
    b.onclick = Timer.action;
    b.onkeypress = Timer.action;
    t.appendChild(d);
    t.appendChild(b);
    return t;
    }

    function setup(i) {
    if (i < 50) {
    Graphics.appendChild(createTimerBox(i));
    window.setTimeout('setup('+(++i)+')',100);
    }
    }

    function init() {
    // If IE then Graphics is already referenced,
    // otherwise create a global reference:
    if ('undefined' == typeof Graphics) {
    Graphics = document.getElementById('Graphics');
    }
    // Fill the screen and start the mess:
    window.setTimeout('setup(0)',100);
    }

    window.onload = init;
    </script>

    <style type="text/css">
    body {
    font: 1em Verdana, sans-serif;
    color: #000000;
    background-color: #F5F5F5;
    margin: 20px 20px;
    padding: 0px 0px}

    var.timer {
    display: -moz-inline-box;
    display: inline-block;
    position: relative;
    left: 0px;
    top: 0px;
    margin: 5px 5px;
    padding: 5px 5px;
    border: medium outset;
    background-color: #CCCCCC;
    font-size: 0.8em;
    font-style: normal;
    font-weight: bold;
    cursor: default}

    var.timer span {
    display: -moz-inline-box;
    display: inline-block;
    position: relative;
    left: 0px;
    top: opx;
    margin: 5px 5px;
    padding: 5px 10px;
    border: medium inset;
    color: #CCFF66;
    background-color: #006600}

    var.timer b {
    display: -moz-inline-box;
    display: inline-block;
    position: relative;
    left: 0px;
    top: 0px;
    margin: 5px 5px;
    padding: 5px 10px;
    border: medium outset;
    cursor: hand;
    cursor: pointer}

    #Graphics {
    position: relative;
    left: 0px;
    top: 0px
    height: auto;
    width: 100%;
    margin: 0px 0px;
    padding: 0px 0px}
    </style>
    </head>

    <body>
    <h1>Thread capacity test</h1>
    <div id="Graphics"></div>
    </body>
    </html>
     
    VK, Mar 6, 2006
    #8
  9. Andrew Poulos

    VK Guest

    Heh... I decided to check if the stuff is still alive and found a bug:

    ....
    msg+= 'Press Cancel to stop, press OK to keep running';
    if (window.confirm(msg)) {
    ....

    replace to:

    ....
    msg+= 'Press Cancel to stop, press OK to keep running';
    if (!window.confirm(msg)) {
    ....

    Otherwise the action doesn't correspond to the chosen option. It's
    strange it did not bother me year ago (? :)
     
    VK, Mar 6, 2006
    #9
  10. VK wrote:

    > Andrew Poulos wrote:
    >> With the following code I can't understand why this.num keeps
    >> incrementing each time I create a new instance of Foo. For each instance
    >> I'm expecting this.num to alert as 1 but keeps incrementing.
    >>
    >> Foo = function(type) {
    >> this.num = 0;
    >> this.type = type
    >> this.trigger();
    >> }
    >> Foo.prototype.trigger = function() {
    >> me = this;
    >> setTimeout("me.test();",1000);
    >> }
    >> Foo.prototype.test = function() {
    >> this.num++;
    >> alert(this.type + "+" + this.num);
    >> }
    >>
    >> f1 = new Foo("a");
    >> f2 = new Foo("b");
    >> f3 = new Foo("c");
    >>
    >> Another aspect I can't resolve is why the alert of this.type is "c" each
    >> time. I guess the issues are being caused by the setTimeout. What's
    >> happening and what's a better way to do this?

    >
    > I don't think you will be ever able to reach what you want in the way
    > you want. window.setTimeout has been implemented waaay in JavaScript
    > 1.0 then no one even thought about constructors, prototypes and stuff.
    >
    Code:
    [/color]
    
    Utter nonsense, as usual.
    
    
    PointedEars
     
    Thomas 'PointedEars' Lahn, Mar 6, 2006
    #10
  11. Andrew Poulos

    VK Guest

    Thomas 'PointedEars' Lahn wrote:
    > Utter nonsense, as usual.


    Utter Thomas, as usual.
     
    VK, Mar 6, 2006
    #11
  12. VK wrote:
    > Andrew Poulos wrote:
    >> With the following code I can't understand why
    >> this.num keeps incrementing each time I create a
    >> new instance of Foo. ...

    <snip>
    > I don't think you will be ever able to reach what you
    > want in the way you want. window.setTimeout has been
    > implemented waaay in JavaScript 1.0 then no one even
    > thought about constructors, prototypes and stuff.
    > He runs with this==window and no amount of braces around
    > can change it. Any attempts to stick some other "this"
    > are going away like water off a duck's back. So if I'm
    > reading your intentions right:
    >
    > {
    > {
    > {
    > ... and somewhere deep in the instance curlies
    > setTimeout running for an instance method
    > ...
    >
    > then NOOP - you cannot do it.
    >
    > There were some extensive discussion back in 90's (not
    > here), and since nothing dramatically changed since then,


    At least two things have changed over the history of - setTimeout -; the
    number of implementations that allow a function reference to be passed
    as the first argument to - setTimeout - has increased, to the point
    where the facility's availability would be the norm, and it has been
    observed that the language's preponderance for automatic type-conversion
    provides a mechanism for supporting implementations where - setTimeout -
    only recognises an initial string argument while only directly working
    with function references arguments.

    The practical result of which is that systems designed to employ the
    manipulation of string arguments for - setTimeout - have tended to be
    relegated to situations where strings have more significance than would
    be normal in modern browser scripts, such as when using -
    document.write - to insert HTML elements who's event handlers want to
    call particular instance methods.

    > you may stick to this solution.


    Of available methods it would be the worst choice in most circumstances,
    so you may well choose to stick to it as choosing the worst option of
    any available in any circumstances does appear to be your habit.

    > You can try to transform setTimeout default into its strength.
    > If it's guranteed to run in the global scope, so give him a
    > reference in a window property - you can be sure that it will
    > pick it up.
    >
    > A sample can be found at
    > <http://www.geocities.com/schools_ring/archives/threads.html>
    > and the code posted at the end of this message - though I'm
    > affraid newsreader may jam everything.


    Most people are capable of learning to control their posting software
    and ensuring that the code they post is acceptably presented.

    > This sample is a kind of overkill, because I created it
    > while testing script engine "thread capacity" plus some
    > dynamic styling discrepancies between browsers. Nevertheless
    > the idea is clear (I hope :)


    It is certainly clear that the idea was not well understood by the
    implementer of the example.

    > 1) You give an OID (Object ID) to each instance.
    > 2) On thread start you add new property to the current
    > window where the key is instance OID and value is instance
    > reference.


    Any runtime stuffing of named properties into the global namespace
    should be enough to flag a notion as misconceived, machine generated
    Identifiers would be the worst candidates for that role as it would be
    less than clear what the offending property names would be at the point
    of insertion and so difficult to see what naming collisions would be
    risked in the process.

    > 3) On thread stop you remove this property.


    That seems unnecessary as the inherent assumption that there will be no
    naming collisions with other aspects of the system (while itself
    reckless) implies that the identifier will not otherwise be used.

    > 4) setTimeout is happy to use window[oid].method.


    But not any more happy than it would be with -
    MyObject.instances[instanceIndex].method(); - and risking no namespace
    collisions.

    > setTimeout is happy - you are happy :)


    Being happy with a bad design where a much superior design is only a
    stone's throw away is a characteristic of a poor script author, and
    symptomatic of an inadequate thought process backup-up with superficial
    testing.

    <snip>
    > function Timer(out) {
    > // Instance class name:
    > this.$name = 'Timer';
    > // Instance OID (Object ID):
    > this.$oid = this.$name + (new Date()).getTime();


    It is known that the resolution of output from Date objects is rarely
    better than 10 milliseconds. This means that if two objects are
    instantiated within the 10 milliseconds over which the output of - (new
    Date()).getTime() - does not change they will be given the same - $oid -
    value, and then if they have activity scheduled with - setTimeout - that
    overlaps at all the results will be chaotic.

    That is chaotic behaviour, but only when three variable 'ifs' are
    satisfied. This is the sort of scripting that follows from an inadequate
    thought process and is not revealed in superficial testing.

    The first variable 'if' is related to the number of objects
    instantiated. The way in which the code is actually used in anger will
    influence its susceptibility to the inadequacies in its design. Only
    insatiate a single object and no problem will ever become apparent.

    The second variable 'if' is time related. Code testing on a relatively
    slow machine may guarantee that no two instances are created within the
    critical interval, but that same code may reliably fail when exposed to
    a significantly faster computer. A testing issue that becomes a
    maintenance issue when you consider that computer hardware has
    demonstrated a strong tendency to become faster over time. So while two
    instances may never be created within the critical interval on current
    computers the odds have got to be good that at some future point
    hardware will become fast enough to expose the script's designed-in
    susceptibility.

    The third variable 'if' is again related to how the code is used. If two
    instances that have been given identical IDs never attempt to schedule
    activity in a way that overlaps no issue will be exposed.

    So we have a design issue that has a predictable capacity for chaotic
    outcomes that will vary in its manifestations depending on how the code
    is actually used, the exact nature (mostly speed) of the hardware it
    executes on, and vary with the precise timing of the commencement of
    execution of the code. That anyone would be satisfied (or even "happy")
    with this situation reveals a failure to understand the code as written
    or a reckless disregard, where a non-joined-up thought process ends at a
    superficial 'working' and no further consideration is given to the code
    at all.

    > Timer.$table[this.$oid] = this;


    And yet just another couple of steps in the though process an you could
    have created an issue-free implementation, and that line was the clue to
    how it might be done.

    The first step was not made because you are willing to disregard decades
    of 'bast practice' advice (probably because it has too many 'academic'
    associations, being explicitly taught in all formal programming
    education) and effectively dynamically add global variables. Any genuine
    programmer would have rejected that idea on principle and looked for a
    way of uniquely referencing object instances that stayed out of the
    global namespace.

    A property of the object's constructor that was an Array or an object
    would satisfy that requirement if used as a storage object for object
    instances. You have created that object, and used it to store such
    references, but failed to follow through to the point of realising that
    it makes adding properties to the window object unnecessary, and so
    avoids all issues that may result from that particular bad practice.
    Instead of:-

    var cmd = 'self[\''+oid+'\'].$tick(\''+oid+'\')';
    $tid = window.setTimeout(cmd,1000);

    -:-

    var cmd = ' Timer.$table[\''+oid+'\'].$tick(\''+oid+'\')';
    $tid = window.setTimeout(cmd,1000);

    - would globally reference the particular object instance with the same
    reliability as the former, but without even involving global namespace
    issues, let alone risking manifestations of them.

    Of course that is still subject to the issues arising from the poor
    choice of - oid - value creation method. That issue follows from the
    superficial though process surrounding the realisation of the need for
    uniqueness in the IDs assigned to the objects. Timers provide a
    uniqueness within a single machine (assuming that the machine's clock is
    not perversely re-set) but with a limited resolution. That is an
    adequate sort of uniqueness for tasks like discouraging machine-local
    HTTP resource caching as the life of the cache is longer than the life
    of the executing program, and the HTTP request/response process is
    unlikely to take less time than the resolution of the timer (though
    faster hardware _and_ internet connections could still make the timer
    resolution an issue in the future).

    The unique identification of an object instance in browser scripts does
    not need to be nearly that unique as navigating away from the current
    page is all that is necessary to invalidate any existing object
    instances. A unique identifier for an object instance does not need to
    be more unique than being unique to the specific instance of the
    execution of the script. For which a simple sequence is sufficient,
    i.e:-

    var uniqueNum = (function(){
    var num = 0;
    return (function(){
    return ++num;
    });
    })();

    - and calls to - uniqueNum() - will return a sequence of
    999999999999999934469 unique integers (and then break down). Quite
    sufficient for use as script-execution unique identifiers (as browser
    scripts would not be expected to run for long enough to get anywhere
    near the point when that sequence ends).

    However, IDs unique to the instance for the execution of the script are
    also more than is necessarily (especially if they are not going to be
    used in the global namespace). It is only necessary that the ID be
    unique within the 'class' of the object instances. Hence the common
    practice of using an array as the instance storage object and its -
    length - property as ID of the object instance. E.G:-

    function MyObject(){
    MyObject.insts[(this.oid = MyObject.insts.length)] = this;
    ...
    }

    MyObject.insts = [];

    MyObject.prototype.setTimer = function(){
    setTimeout(('MyObject.insts['+this.oid+'].doSomething();'), 40);
    };

    MyObject.prototype.doSomething = function(){
    ...
    };

    And now all the issues are gone. No two object instances will have the
    same ID during a single execution of the script, ever, and there is not
    even a chance of a global namespace collision.

    The design process doesn't have to be carried that much further to go
    from the superficially 'working' to the reliable.

    > // Output (DOM element):
    > this.$out = out || null;
    > // Timer ID:
    > this.$tid = null;


    Constant values assigned to each object instance are more effectively
    created as properties of the constructor's prototype, as then all
    instances are defaulted to the same value implicitly.

    > // Minutes counter:
    > this.$min = 0;


    Ditto.

    > // Seconds counter:
    > this.$sec = 0;


    Ditto.

    > // Static methods (shared between instances):


    "Static" methods are method 'of the class'. These are instance methods.
    You can tell that they are instance methods because they use the -
    this - keyword to refer to the object instance, and would not work if
    executed in any way other than as a method of the object instance.

    > this.start = Timer.$start;


    Again, it is more efficient to create public instance methods as
    properties of the constructor's prototype and have then inherited by
    each object instance implicitly. It also makes for much clearer code as
    the reader does not have to reconcile all (or worse, and in this case, a
    sub-set of) the methods assigned to properties of the constructor as
    object instance methods.

    > this.$tick = Timer.$tick;


    Ditto.

    > this.stop = Timer.$stop;


    Ditto.

    > this.toString = Timer.toString;


    Ditto.

    > }
    >
    > Timer.$table = new Object();
    >
    > Timer.$start = function() {
    > // Start timer instance:
    > if (this.$tid == null) {
    > var oid = this.$oid;
    > var cmd = 'self[\''+oid+'\'].$tick(\''+oid+'\')';


    Notice that the - $tick - method is to be executed as a method of the
    object instance by the setTimeout call, and if it were not that method
    would not execute as expected

    > self[oid] = this;


    As - Timer.$table[this.$oid] - will resolve as a reference to the object
    instance form any context where 'Table' has not been locally declared
    (and it hasn't anywhere here in practice, and would not be expected to
    be in principle as the initial capital letter is usually used to
    highlight a 'class' constructor) there is no reason to be adding a
    reference to - this - as a property of the window object.

    > this.$tid = window.setTimeout(cmd,1000);
    > return this.$oid;
    > }
    > }
    >
    > Timer.$tick = function(oid) {
    > // OnTimer instance call:
    > with (self[oid]) {


    As this function is executed as a method of an object instance (and must
    be or it will behave in an implementation dependent way) there is no
    need to refer to that object instance with - window[oid], and indeed no
    need to pass the - oid - parameter at all. Using - with(this){ - would
    be sufficient, if pointless as all the object properties can be
    reference through the - this - keyword itself, and the result would be
    muck clearer code.

    > $sec++;
    > if ($sec >= 60) {
    > $min++;
    > $sec = 0;
    > }
    > if ($out != null) {
    > $out.innerHTML = this.toString($min,$sec);


    You ca see here that the function must be executed as a method of the
    object instance as otherwise the - this - keyword would refer to the
    global object, which may or may not have a - toString - method, and if
    it does it will not do what is expected with the arguments provided.

    > }
    > var cmd = 'self[\''+oid+'\'].$tick(\''+oid+'\')';
    > $tid = window.setTimeout(cmd,1000);
    > }
    > }
    >
    > Timer.$stop = function() {
    > // Stop timer instance:
    > if (this.$tid != null) {
    > window.clearTimeout(this.$tid);
    > self[this.$oid] = null;


    Having realised that there is no need for a global reference to the
    object instance the effort of creating one, and subsequently nulling it
    can be entirely avoided. There was never any good reason for nulling it
    anyway as no code here acts conditionally based upon its existence.

    > this.$tid = null;
    > }
    > }
    >
    > Timer.toString = function(m,s) {
    > var t = '';
    > if (arguments.length < 2) {
    > t = 'function';
    > }
    > else {
    > t = (m < 10)? '0'+m : m;
    > t+= ':';
    > t+= (s < 10)? '0'+s : s;
    > }
    > return t;
    > }


    Given that the last week has shown you suffering from misinterpreting
    you own code where the omission of a statement-terminating semicolon
    radically altered the meaning of the code in a way that was beyond your
    conception, it would be a good idea for you to adopt the practice of
    explicitly terminating statements with semicolons, including the
    function expression assigning ExpressionStatements here. While you don't
    know the rules for automatic semicolon insertion you cannot understand
    when semicolons may be safely omitted.

    > Timer.bDown = function(e) {


    And this is where defining object instance methods as properties of the
    constructor shows its folly, as these methods are 'of the class'. The
    reader has to perceive that distinction for themselves instead of having
    it made for them in the code as written. So the runtime overheads in not
    employing the constructor's prototype is compounded by an ongoing
    maintenance burden that would be completely avoidable.

    > // Visual change of pseudo-button on mousedown:
    > var evt = e || event;
    > if (evt) {
    > var trg = evt.target || evt.srcElement;
    > trg.style.borderStyle = 'inset';
    > }
    > }

    <snip>

    These functions are all assigned to intrinsic event handling properties
    of the created DOM elements. As such the - this - keyword will reliably
    refer to the constructed element when they are executed, making the use
    of - evt.target || evt.srcElement - to acquire a reference to that
    element redundant, and potentially problem-causing as an event
    intercepted during bubbling will not necessarily have the correct
    element as its target or srcElement.

    > Timer.action = function(e) {
    > // Display current instance data and options:
    > var evt = e || event;
    > if (evt) {
    > var trg = evt.target || evt.srcElement;
    > var oid = trg.title;


    So - trg.title - may not have the expected value.

    > var tmp = Timer.$table[oid];


    And so - Timer.$table[oid]; - may not return a valid instance reference.

    > var msg = 'Instance OID: ' + oid +'\n\n';
    > var val = tmp.toString(tmp.$min,tmp.$sec);


    And if - tmp - is not a valid instance reference the code will likely
    error-out here.

    Learning the basics of javascript, which includes knowing how to
    determine the predictable value of the - this - keyword depending on the
    way in which a function is executed (instead of throwing up you hands in
    horror and declaring - this - to be incontinent) combined with a more
    joined-up thought process would significantly improve the code you
    write. Which currently, as has been pointed out many times before,
    usually represents the worst possible implementation of the worst
    approach available. The code here is no exception.

    Richard.
     
    Richard Cornford, Mar 13, 2006
    #12
  13. JRS: In article <dv325i$18j$1$>, dated Mon, 13
    Mar 2006 06:06:41 remote, seen in news:comp.lang.javascript, Richard
    Cornford <> posted :


    >It is known that the resolution of output from Date objects is rarely
    >better than 10 milliseconds.


    To clarify : the resolution of Date Objects is by specification one
    millisecond. But new Date() gets a value from the OS, and it is
    that which is often 10 ms or 50/60 ms.

    <URL:http://www.merlyn.demon.co.uk/js-dates.htm#Ress> gives results for
    various browsers. However, only the result for Win98/IE4 was measured
    by me ...




    >var uniqueNum = (function(){
    > var num = 0;
    > return (function(){
    > return ++num;
    > });
    >})();
    >
    >- and calls to - uniqueNum() - will return a sequence of
    >999999999999999934469 unique integers (and then break down).


    9007199254740992 ??? (2^53) - Actually, with num++ you'd get one
    more?

    --
    © John Stockton, Surrey, UK. ?@merlyn.demon.co.uk Turnpike v4.00 IE 4 ©
    <URL:http://www.jibbering.com/faq/> JL/RC: FAQ of news:comp.lang.javascript
    <URL:http://www.merlyn.demon.co.uk/js-index.htm> jscr maths, dates, sources.
    <URL:http://www.merlyn.demon.co.uk/> TP/BP/Delphi/jscr/&c, FAQ items, links.
     
    Dr John Stockton, Mar 13, 2006
    #13
    1. Advertising

Want to reply to this thread or ask your own question?

It takes just 2 minutes to sign up (and it's free!). Just click the sign up button to choose a username and then you can ask your own questions on the forum.
Similar Threads
  1. Kenneth McDonald
    Replies:
    5
    Views:
    343
    Kenneth McDonald
    Sep 26, 2008
  2. Ken
    Replies:
    4
    Views:
    118
    Michael Winter
    Oct 7, 2004
  3. Telmo Costa

    Object this and setTimeout

    Telmo Costa, Feb 21, 2006, in forum: Javascript
    Replies:
    3
    Views:
    99
    Thomas 'PointedEars' Lahn
    Feb 22, 2006
  4. Replies:
    2
    Views:
    84
  5. -Lost
    Replies:
    7
    Views:
    188
    -Lost
    Mar 24, 2007
Loading...

Share This Page