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>