开发者

Closure fail - even after reading all about it

开发者 https://www.devze.com 2023-04-07 09:16 出处:网络
I have this countdown fiddle here I would LIKE to make the following work in an onload but I obviously have a problem with closures

I have this countdown

fiddle here

I would LIKE to make the following work in an onload but I obviously have a problem with closures

for (var o in myDates) {
  var myDate = myDates[o];
  var iid = o;
  funcs[o] = function() {
    var dateFuture = new Date();
    dateFuture.setSeconds(dateFuture.getSeconds()+myDate.durationInSecs);
    GetCount(dateFuture,iid);    
  }
  myDates[iid].tId = setTimeout("funcs['"+iid+"']()",myDates[o].delay*1000);
}

The code below works. BUT it has an implicit eval and 2 global vars and I would like to loop like the non-working code above


<html>
<head>
<script type="text/javascript">

var myDates = {
    d0: {
      durationInSecs: 5, 
      delay:0,
      repeat:false,
      duringMessage : " until something happens",
      endMessage    : " something happened",      
      repeatMessage : ""
    }, 
    d1: { 
      durationInSecs: 10, 
      delay:3,
      repeat:true,
      duringMessage : " until something else happens",
      endMessage    : " something else happened",
      repeatMessage : "This will repeat in 3"
    }
}
var funcs = {};

window.onload=function(){
  // the below could be done in a loop, if I was better in closures
  setTimeout(function() {
    funcs["d0"] = function() {
      var myDate = myDates["d0"];    
      var dateFuture0 = new Date();
      dateFuture0.setSeconds(dateFuture0.getSeconds()+myDate.durationInSecs);
      GetCount(dateFuture0,"d0");    
    }
    funcs["d0"]();
  },myDates["d0"].delay*1000);
  // ---------------
  setTimeout(function() {
    funcs["d1"] = function() {
      var myDate = myDates["d1"];
      var dateFuture1 = new Date();
      dateFuture1.setSeconds(dateFuture1.getSeconds()+myDate.durationInSecs);
      GetCount(dateFuture1,"d1");    
    }
    funcs["d1"]();    
  },myDates["d1"].delay*1000);
};


//######################################################################################
// Author: ricocheting.com
// Version: v2.0
// Date: 2011-03-31
// Description: displays the amount of time until the "dateFuture" entered below.

// NOTE: the month entered must be one less than current month. ie; 0=January, 11=December
// NOTE: the hour is in 24 hour format. 0=12am, 15=3pm etc
// format: dateFuture1 = new Date(year,month-1,day,hour,min,sec)
// example: dateFuture1 = new Date(2003,03,26,14,15,00) = April 26, 2003 - 2:15:00 pm



// TESTING: comment out the line below to print out the "dateFuture" for testing purposes
//document.write(dateFuture +"<br />");
//###################################
//nothing beyond this point
function GetCount(ddate,iid){
    dateNow = new Date();   //grab current date
    amount = ddate.getTime() - dateNow.getTime();   //calc milliseconds between dates
    delete dateNow; // is this safe in IE?

  var myDate = myDates[iid]; 
    // if time is already past
    if(amount < 0){
        document.getElementById(iid).innerHTML=myDate.endMessage;
        if (myDate.repeat) {
      setTimeout(funcs[iid],myDate.delay*1000);
      document.getElementById(iid).innerHTML=myDate.repeatMessage;
    } 
    }
    // else date is still good
    else{
        secs=0;out="";

        amount = Math.floor(amount/1000);//kill the "milliseconds" so just secs

        secs=Math.floor(amount);//seconds

        out += secs +" "+((secs==1)?"sec":"secs")+", ";
        out = out.substr(0,out.length-2);
        document.getElementById(iid).innerHTML=out + myDate.duringMessage;
        d开发者_如何学Pythonocument.title=iid;
        setTimeout(function(){GetCount(ddate,iid)}, 1000);
    }
}

</script>
</head>
<body>
<div>
<span id="d0" style="background-color:red">!!</span>
<span style="float:right¨;background-color:green" id="d1">??</span>
</div>
</body>


You fell into the trap of declaring a closure inside a loop. The inner function closes ever the variable myDate and not over its value. This is particularly unexpected since variables are supposed to have block scope and not persist over multiple iterations (not the case in Javascript - everything is function scope).

You can work around this by having a closure-maker function. This way you can create a new instance of the variable to close over at will.

WRONG

var i;
for(i=0; i<xs.length; i++){
    something = function(){
        f(i);
    };
}
//all closures share the i variable and will have it
//be i=xs.length in the end. We don't want that.

OK

function make_handler(i){
    return function(){
        f(i);
    };
    //each call gets its own copy of i
}
var i;
for(i=0; i<xs.length; i++){
    something = make_handler(i);
}

or have the closure-maker inline

var i;
for(i=0; i<xs.length; i++){
    something = (function(i){
        return function(){
            f(i);
        };
    }(i));
}
0

精彩评论

暂无评论...
验证码 换一张
取 消

关注公众号