setTimeout(this.doAnimation, 1000) will fail if it is defined in aclass definition

  • Thread starter liketofindoutwhy
  • Start date
L

liketofindoutwhy

I did some animation using


var oDiv = {};
oDiv.x = 100;
oDiv.y = 100;
oDiv.node = document.getElementById('divImage');
oDiv.node.style.position = "absolute";

oDiv.doAnimation = function() {
oDiv.x += 1;
oDiv.y += 1;
oDiv.node.style.top = oDiv.y + 'px';
oDiv.node.style.left = oDiv.x + 'px';
setTimeout(oDiv.doAnimation, 33);
}
and it works fine. When it is changed to the traditional class
definition, then it fails:


function Animation() {
this.x = ...
etc...

this.doAnimation = function() {
// do something

// set delay
setTimeout(this.doAnimation, 33);
}
}

oDiv = new Animation();
oDiv.doAnimation();
which will fail. The second time when the doAnimation() is called, it
seems that it doesn't have a concept of this.x, etc.

It is the same if I use the more popular way of defining a class by
moving the function out:


function Animation() {
this.x = ...
etc...
}

Animation.prototype.doAnimation = function() {
// do something
}
and it is the same, the second time when doAnimation() is invoked by
the setTimeout, it doesn't have a concept of this.x.

Does someone know what it is? hm... I don't need to use prototype.js
and use something like doAnimation.bind(this) ? Can it be a self-
contained script that doesn't rely on other framework? Thanks.
 
G

Gregor Kofler

liketofindoutwhy meinte:
and it works fine. When it is changed to the traditional class
definition, then it fails:

There are no classes in JS.
function Animation() {
this.x = ...
etc...

this.doAnimation = function() {
// do something

// set delay
setTimeout(this.doAnimation, 33);
}
}

oDiv = new Animation();
oDiv.doAnimation();
which will fail. The second time when the doAnimation() is called, it
seems that it doesn't have a concept of this.x, etc.
[snip]

Does someone know what it is?

You need a closure. Google "javascript closure" and read.

You will eventually end up with something like

var that = this;
....
window.setTimeout(that.doAnimation, 33);

Gregor
 
G

Gregor Kofler

Thomas 'PointedEars' Lahn meinte:
Gregor Kofler wrote:

Should be

window.setTimeout(function() { that.doAnimation() }, 33);

Oops. Sorry. My bad. My version does exactly _not_ create a closure.

Gregor
 
T

Thomas 'PointedEars' Lahn

Gregor said:
liketofindoutwhy meinte:
function Animation() {
this.x = ...
etc...

this.doAnimation = function() {
// do something

// set delay
setTimeout(this.doAnimation, 33);
}
}

oDiv = new Animation();
oDiv.doAnimation();
which will fail. The second time when the doAnimation() is called, it
seems that it doesn't have a concept of this.x, etc.
[snip]

Does someone know what it is?

You need a closure. Google "javascript closure" and read.

You will eventually end up with something like

var that = this;
...
window.setTimeout(that.doAnimation, 33);

Should be

window.setTimeout(function() { that.doAnimation() }, 33);

because with your code the method loses its connection to `that' (being
passed as just another Function object reference) and `this' in its
execution context will again refer to the Window or Global Object instead.


PointedEars
 
L

liketofindoutwhy

wow, that's a lot of this and that... and by the way, is it just
Javascript? Do other language automatically say... ok... it is
this.doAnimation, so when I call doAnimation, I will make sure it is
applied on the current object "this" is referring to.


by the way, someone gave me a solution (besides using Prototype.js
setTimeout(this.doAnimation.bind(this), 33);

define a global function:

function bindIt(obj, func) {
return function() { func.apply(obj) }
}

and then use

setTimeout(bindIt(this, this.doAnimation), 33);
 
J

Jorge

wow, that's a lot of this and that...  and by the way, is it just
Javascript?  Do other language automatically say... ok... it is
this.doAnimation, so when I call doAnimation, I will make sure it is
applied on the current object "this" is referring to.

by the way, someone gave me a solution (besides using Prototype.js
setTimeout(this.doAnimation.bind(this), 33);

define a global function:

        function bindIt(obj, func) {
            return function() { func.apply(obj) }
        }

and then use

setTimeout(bindIt(this, this.doAnimation), 33);

You could as well do:

function Animation () {
var that= this;
this.x = ...
etc...
this.doAnimation = function () {
//use 'that' instead of 'this'
// do something
// set delay
setTimeout(arguments.callee, 33);
};
};

but then doAnimation() is permanently bound to 'that'.
 
T

Thomas 'PointedEars' Lahn

liketofindoutwhy said:
wow, that's a lot of this and that... and by the way, is it just
Javascript? Do other language automatically say... ok... it is
this.doAnimation, so when I call doAnimation, I will make sure it is
applied on the current object "this" is referring to.

You have failed to phrase an intellible question.
by the way, someone gave me a solution (besides using Prototype.js
setTimeout(this.doAnimation.bind(this), 33);

define a global function:

function bindIt(obj, func) {
return function() { func.apply(obj) }
}

and then use

setTimeout(bindIt(this, this.doAnimation), 33);

That's (Prototype.js) junk, not a solution. To begin with, it requires
Function.prototype.apply() to be supported, or it breaks, needlessly.


PointedEars
 
O

Oltmans

Gregor said:
liketofindoutwhy meinte:
function Animation() {
this.x = ...
etc...
this.doAnimation = function() {
// do something
// set delay
setTimeout(this.doAnimation, 33);
}
}
oDiv = new Animation();
oDiv.doAnimation();
which will fail. The second time when the doAnimation() is called, it
seems that it doesn't have a concept of this.x, etc.
Does someone know what it is?
You need a closure. Google "javascript closure" and read.
You will eventually end up with something like
var that = this;
...
window.setTimeout(that.doAnimation, 33);

Should be

window.setTimeout(function() { that.doAnimation() }, 33);

because with your code the method loses its connection to `that' (being
passed as just another Function object reference) and `this' in its
execution context will again refer to the Window or Global Object instead.

I'm still not sure about one thing. Why doesn't
window.setTimeout(this.doAnimation,33) work? Is it because of the fact
that when JavaScript interpreter will come across this line it will
set "this" in "this.doAnimation" line to global window object as
setTimeout() is a method of global object? Does that sound good?
Moreover, having a closure like window.setTimeout(function()
{that.doAnimation()},33) works because Closures remember their context
and this is why "that" will point to correct "this"?

Can you please expand on that a bit? I will appreciate your help.

Thanks.
 
J

Jorge

I'm still not sure about one thing. Why doesn't
window.setTimeout(this.doAnimation,33) work? Is it because of the fact
that when JavaScript interpreter will come across this line it will
set "this" in "this.doAnimation" line to global window object as
setTimeout() is a method of global object?

No.

1.- Functions are objects, and the objects are passed by reference.
setTimeout expects (as a parameter) a reference to a function.

2.- 'this.doAnimation' is a property (of the object that 'this' points
to) that happens to hold a reference to a function.

3.- It is that reference what gets passed to setTimeout, and it holds
no relation with 'this' nor with the object that 'this' points to nor
with its 'doAnimation' property. That we have read it in the property
'doAnimation' of the object that 'this' points to, it doesn't matter.
Where it comes from is a don't care, it's just a reference to a
function.

Moreover, having a closure like window.setTimeout(function()
{that.doAnimation()},33) works because Closures remember their context
and this is why "that" will point to correct "this"?

1.- As the function (*) 'function() {that.doAnimation()}' has been
created in the context of the outer function, it too has access to the
same context.

2.- As long as a reference to that function (*) exists (in this case,
the reference that exists is the one passed to setTimeout), the
context won't/can't be destroyed by the garbage collector.

3.- And as 'that' is defined in the context, it's accesible from the
function (*) as well.
 
J

Jorge

No.

1.- Functions are objects, and the objects are passed by reference.
setTimeout expects (as a parameter) a reference to a function.

2.- 'this.doAnimation' is a property (of the object that 'this' points
to) that happens to hold a reference to a function.

3.- It is that reference what gets passed to setTimeout, and it holds
no relation with 'this' nor with the object that 'this' points to nor
with its 'doAnimation' property. That we have read it in the property
'doAnimation' of the object that 'this' points to, it doesn't matter.
Where it comes from is a don't care, it's just a reference to a
function.

And:

4.- If we call 'f' the reference to a function, then that's what your
code does:

this.doAnimation= f; -> setTimeout(this.doAnimation, 33) ===
setTimeout(f, 33) -> when setTimeout times out it calls f().
 
T

Thomas 'PointedEars' Lahn

Oltmans said:
I'm still not sure about one thing. Why doesn't
window.setTimeout(this.doAnimation,33) work?

I thought I had explained that above already.
Is it because of the fact that when JavaScript interpreter will come
across this line it will set "this" in "this.doAnimation" line to
global window object as setTimeout() is a method of global object?

No. First of all, the JavaScript interpreter (Virtual Machine) does not see
any of this. JavaScript source code is compiled into byte-code, which is
then interpreted/executed by a VM, just like Java (only that it is usually
JIT-compilation with JS).

Second, the compiler does not modify the meaning of symbols based on their
context. It is instead that if you pass `this.doAnimation' you are merely
passing a Function object reference (provided that `this.doAnimation' refers
to a Function object). When this Function object is called (here: by the
object referred to by `window') there is no telling which object's property
referred to it. So the `this' value initially does not refer to any object
(`null') which means that it is then set to the Global Object (that is not
necessarily the same as the object referred to by `window'). And that
object does not have a `doAnimation' property in either case.
Does that sound good?

Unfortunately, no.
Moreover, having a closure like window.setTimeout(function()
{that.doAnimation()},33) works because Closures remember their context
and this is why "that" will point to correct "this"?

Not quite. The function (expression) creates a closure, because there is
literally a bound variable here, `that'. It is a bound variable because
that variable is declared outside the function, and therefore its usage is
bound to that outer area. That outer area is the closure's definition
context (the context in which it was defined). That context is reproduced
when the function is called. Since `that' is (the identifier of) a variable
that has been assigned the `this' value within the definition context, it
stores a reference to the same object that `this' referred to, there and
then. That reference -- no pun intended -- can be used within the called
function then, no matter the caller.

Please trim your quotes to the necessary minimum, as you can see here.


PointedEars
 
T

Thomas 'PointedEars' Lahn

Jorge said:
No.

1.- Functions are objects, and the objects are passed by reference.

No, all arguments are passed by value; object references are values.[1]
setTimeout expects (as a parameter) a reference to a function.

Or a string value, whereas the latter is slightly more compatible.
2.- 'this.doAnimation' is a property (of the object that 'this' points
to) that happens to hold a reference to a function.

[1^] See?
2.- As long as a reference to that function (*) exists (in this case,
the reference that exists is the one passed to setTimeout), the
context won't/can't be destroyed by the garbage collector.

True, however an implementation is free to provide a copy instead of a
reference, which would allow the source of this copy to be marked for
garbage collection.


PointedEars
 
O

Oltmans

Not quite. The function (expression) creates a closure, because there is
literally a bound variable here, `that'. It is a bound variable because
that variable is declared outside the function, and therefore its usage is
bound to that outer area. That outer area is the closure's definition
context (the context in which it was defined). That context is reproduced
when the function is called. Since `that' is (the identifier of) a variable
that has been assigned the `this' value within the definition context, it
stores a reference to the same object that `this' referred to, there and
then. That reference -- no pun intended -- can be used within the called
function then, no matter the caller.
OK. Thank you for explaining this. Very helpful. One last question.
setTimeout() expects function as its first argument. I guess both of
the following will work

a) window.setTimeout(aFunc,50);
where aFunc is defined as

function aFunc(){
alert('you will see me again');
}

b) window.setTimeout(function(){alert('you will see me again');},50);

My question is that if we can pass the Function Object Reference as
depicted in (a) then is it simply replaced with the function body e.g.
aFunc will be relpaced with the function aFunc(){alert('you will see
me again');}.

I mean if both (a) and (b) can do the job then why are there two ways
to do the same thing in the language. Any examples where passing
"Function object references" makes a difference while coding the
function as a parameter doesn't or vice-versa?

Please pardon my ignorance but I really am not sure about this.
 
D

dhtml

Oltmans said:
OK. Thank you for explaining this. Very helpful. One last question.
setTimeout() expects function as its first argument. I guess both of
the following will work

a) window.setTimeout(aFunc,50);
where aFunc is defined as

function aFunc(){
alert('you will see me again');
}

This one creates a property of the variable object, so you can use it
again and again.

aFunc();

setTimeout(aFunc, 100);

b) window.setTimeout(function(){alert('you will see me again');},50);

This one can't be referred again. It's called an 'anonymous' function.

The former has an advantage in that if you want to say, remove an event
listener.

aDiv.addEventListener('click', aFunc, true);

If an anonymous function were used, it would not be possible to remove
it like so:-

aDiv.removeEventListener('click', aFunc, true);

My question is that if we can pass the Function Object Reference as
depicted in (a) then is it simply replaced with the function body e.g.
aFunc will be relpaced with the function aFunc(){alert('you will see
me again');}.

No, it is not replaced. A value of the reference to aFunc is passed.

So, you could also have:-

function aFunc() {
arguments.callee.t = new Date;
}

setTimeout(aFunc, 100);

setTimeout(function() {
alert('aFunc called on '+ aFunc.t );
}, 200);

I mean if both (a) and (b) can do the job then why are there two ways
to do the same thing in the language. Any examples where passing
"Function object references" makes a difference while coding the
function as a parameter doesn't or vice-versa?

If you don't need the function hanging around any more, just pass a
function expression.

It is possible (though not always advisable) add an optional Identifier
to the Function Expression.

setTimeout(function callback() { alert(arguments.callee); }, 10);

That is different from a FunctionDeclaration in a few ways. However,
there is a bug in IE that causes a FunctionExpression with an identifier
to be parsed as a both as a FunctionDeclaration and a
FunctionExpression. It's best to be aware of that bug in IE. This has
been discussed in the archives and does come up fairly frequently (it
was discussed recently).

Another benefit of using an identifier (as in a FunctionDeclaration) is
that it will show up in firebug using console.log(aFunc);

function aFunc() {
arguments.callee.t = new Date;
}

var aFunc2 = function () {
arguments.callee.t = new Date;
}
console.log(aFunc);
console.log(aFunc2);

FunctionDeclaration gets evaluated before statements get executed, so it
is safe to refer to it before it is declared:-

window.addEventListener('load', pageLoaded, false);

function pageLoaded() {
alert('done');
}


But that order would not work if a FunctionExpression had been used.

var loaded = function() {
alert('page done');
}

See the Ecma-262 r3 specification. You can find an unofficial copy in HTML:
http://bclary.com/2004/11/07/
 
D

dhtml

dhtml said:
The former has an advantage in that if you want to say, remove an event
listener.

aDiv.addEventListener('click', aFunc, true);

If an anonymous function were used, it would not be possible to remove
it like so:-

aDiv.removeEventListener('click', aFunc, true);


Well technically the fucntion reference can be retained, but only
through being called:-

var b = document.body;
b.addEventListener('click', function(e){
alert(e);
b.removeEventListener('click', arguments.callee, false);
}, false);

by using arguments.callee in the call.
 
J

Jorge

OK. Thank you for explaining this. Very helpful. One last question.
setTimeout() expects function as its first argument. I guess both of
the following will work

a) window.setTimeout(aFunc,50);
where aFunc is defined as

function aFunc(){
alert('you will see me again');

}

b) window.setTimeout(function(){alert('you will see me again');},50);

My question is that if we can pass the Function Object Reference as
depicted in (a) then is it simply replaced with the function body e.g.
aFunc will be relpaced with the function aFunc(){alert('you will see
me again');}.

I mean if both (a) and (b) can do the job then why are there two ways
to do the same thing in the language. Any examples where passing
"Function object references" makes a difference while coding the
function as a parameter doesn't or vice-versa?


1.- A new, distinct instance of a function is created whenever a
function expression is encountered:

var fCollection= [], n= 1;

do {
fCollection[n]= function () { ; };
} while (n--)

alert(fCollection[0] === fCollection[1]); -> false

But:

var fCollection= [], n= 1, f= function () { ; };

do {
fCollection[n]= f;
} while (n--)

alert(fCollection[0] === fCollection[1]); -> true (obviously)


2.- Even when it's 'coded as a parameter', a reference to it is what
gets passed.

3.- The function 'coded as a parameter' gets created in the context in
which the function expression appears.

3.- Therefore, when 'coded as a parameter' you can tell for sure that
the function that gets passed has access to the context from which
you're passing it.

4.- But that might not be so if you're passing a function that was
created previously (one not 'coded as a parameter'):

function alertName () {
alert(typeof aName);
};

function test () {
var aName= "ok";
setTimeout(alertName, 0);
};

test(); -> "undefined"

But:

function test () {
var aName= "ok";
function alertName () {
alert(typeof aName);
};
setTimeout(alertName, 0);
};

test(); -> "string"
 
J

Jorge

(..) The function (expression) creates a closure, because there is
literally a bound variable here, `that'. (..)


This phrase is a bit misleading: closures are created regardless of
variables, whenever an inner function is created in a context and it's
made available from outside of the context, and not because "there is
literally a bound variable".

<script>
window.onload= function () {
var that= '"that" is captured in a closure';

//No bound variables in the function expression:
setTimeout(function(){ alert(eval('th'+'at')) },500);
};
</script>
 
J

Jorge

Well technically the function reference can be retained, but only
through being called:-

var b = document.body;
b.addEventListener('click', function(e){
alert(e);
b.removeEventListener('click', arguments.callee, false);

}, false);

by using arguments.callee in the call.

Or saving a reference to it in the enclosing context:

var b= document.body, fReference;

b.addEventListener('click', (fReference= function(e){
alert(e);
b.removeEventListener('click', fReference, false);
}), false);
 
O

Oltmans

Or saving a reference to it in the enclosing context:

var b= document.body, fReference;

b.addEventListener('click', (fReference= function(e){
alert(e);
b.removeEventListener('click', fReference, false);

}), false);

Thank you, Jorge and dhtml. Appreciate your time and help.
 
T

Thomas 'PointedEars' Lahn

Jorge said:
This phrase is a bit misleading: closures are created regardless of
variables, whenever an inner function is created in a context and it's
made available from outside of the context, and not because "there is
literally a bound variable".

"Literally" is merely emphasizing the fact that it does not need to be a
variable in ECMAScript implementations:

<script>
window.onload= function () {
var that= '"that" is captured in a closure';

//No bound variables in the function expression:
setTimeout(function(){ alert(eval('th'+'at')) },500);
};
</script>

Your example is not Valid and it is error-prone. That aside, `that' *is*
the bound variable here. It does not matter that the identifier does not
occur as-is in the source code of the called function.


PointedEars
 

Ask a Question

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

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,744
Messages
2,569,482
Members
44,901
Latest member
Noble71S45

Latest Threads

Top