ext版本2.1
1.今天同学让我检查一段代码,大意如下:
// do something 0 (function(){ // do something 1 }).defer(1000); (function(){ // do something2 }).defer(1000);
他的意图是,先做第0件事,然后延迟1秒做第一件事情;做完第一件事之后,延迟一秒做第二件事。
2. 问题出现在something2比something1延迟不到1秒钟,代码如下:
var start = new Date().getTime(), end, dur; console.log('开始的时间是:'+ start); // something 0 (function(){ // mark A end = new Date().getTime(); dur = end - start; console.log('第一次defer执行的时间为:' + end + ' 时间差为:' + dur); }).defer(1000); (function(){ // mark B start = new Date().getTime(); dur = start - end; console.log('第二次defer执行的时间为:' + start + ' 时间差为:' + dur); }).defer(1000);
结果如下图:
由图可以看出,something2比something仅仅延迟1毫秒。something1比something0延迟将近3秒。
3. 后者是因为,something1计时器到的时候,浏览器被阻塞了,导致了something1的拖后执行。
代码如下:
var start = new Date().getTime(), end, dur; console.log('开始的时间是:'+ start); // something 0 alert('10秒钟之后再点我!'); // 浏览器阻塞 (function(){ // mark A end = new Date().getTime(); dur = end - start; console.log('第一次defer执行的时间为:' + end + ' 时间差为:' + dur); }).defer(1000); (function(){ // mark B start = new Date().getTime(); dur = start - end; console.log('第二次defer执行的时间为:' + start + ' 时间差为:' + dur); }).defer(1000);
在something1和something0之间加入了一个alert方法,他会阻塞浏览器,结果如下图:
4. something1延迟了17秒多。注:这只是表象,实际上alert()事件阻止了浏览器,导致mark A不能被扫描,事件不能被执行。当alert()接触阻止的时候,浏览器会继续扫描、执行mark A,并设置计时器,1秒钟之后执行内部方法。所以,无论alert()阻止多长时间,mark A内部的方法都会执行,并且比扫描时间延迟1秒钟执行。
5. 一个更明显一点儿的例子:
var start = new Date().getTime(), end, dur; console.log('开始的时间是:'+ start); // something 0 (function(){ // mark A end = new Date().getTime(); dur = end - start; console.log('第一次defer执行的时间为:' + end + ' 时间差为:' + dur); }).defer(1000); (function(){ // mark B alert('10秒钟之后再点我!'); // 浏览器阻塞 start = new Date().getTime(); dur = start - end; console.log('第二次defer执行的时间为:' + start + ' 时间差为:' + dur); }).defer(1000);
把alert放到了something2中,结果如下图:
6. 会不会在阻止浏览器执行期间,计时器时间到而执行延迟方法呢?代码修改如下:
var start = new Date().getTime(), end, dur; console.log('开始的时间是:'+ start); // something 0 (function(){ // mark A end = new Date().getTime(); dur = end - start; console.log('第一次defer执行的时间为:' + end + ' 时间差为:' + dur); }).defer(1000); (function(){ // mark B start = new Date().getTime(); dur = start - end; console.log('第二次defer执行的时间为:' + start + ' 时间差为:' + dur); }).defer(1000); alert('10秒钟之后再点我!'); // 浏览器阻塞
浏览器会顺序扫描代码,把mark A放入事件队列,然后把mark B 放入事件队列,再执行alert阻塞浏览器。等待几秒钟之后,解除浏览器。然后something1和something2会按照顺序分别执行(没有再次延时1秒)。
结果如下图:
小结:浏览器阻止,会导致计时器事件的拖后执行,而不是按照计时器时间执行,也不是不执行。原因,可能跟事件队列有关系,并且虽然拦浏览器被阻止,但是计时器仍然正常计时,时间到的时候,会把事件放入到事件队列。一旦浏览器接触阻止,便会依次执行事件队列的事件。这是个人的想法,以后再考证。
上面说的是阻塞浏览器对计时器的影响,计时器是写在ext defer方法中的。
7. Ext defer 定义如下:
defer : function(millis, obj, args, appendArgs){ var fn = this.createDelegate(obj, args, appendArgs); // 事件代理,this指向的是调用defer的方法,如下例的sayHi if(millis){ return setTimeout(fn, millis); // 重点:计时器 } fn(); return 0; }
简单的例子:
var sayHi = function(name){ alert('Hi, ' + name); }; // executes after 2 seconds: sayHi.defer(2000, this, ['Fred']);
defer方法的重点就是计时器,在这之前产生一个事件代理,代码如下:
createDelegate : function(obj, args, appendArgs){ var method = this; // 注意 return function() { var callArgs = args || arguments; if(appendArgs === true){ callArgs = Array.prototype.slice.call(arguments, 0); callArgs = callArgs.concat(args); }else if(typeof appendArgs == "number"){ callArgs = Array.prototype.slice.call(arguments, 0); // copy arguments first var applyArgs = [appendArgs, 0].concat(args); // create method call params Array.prototype.splice.apply(callArgs, applyArgs); // splice them in } return method.apply(obj || window, callArgs); // 注意,绑定对象 }; }
事件代理的作用就是给事件的this绑定对象,给事件传递参数。
8. JS setTimeout:延迟调用方法
语法如下:
var timeoutID = window.setTimeout(func, delay, [param1, param2, ...]); var timeoutID = window.setTimeout(code, delay);
例子如下:
var Person = function(){ this.addr = 'wy'; this.age = '18'; this.sayHi = function(){ console.log('addr:' + this.addr +' age:' + this.age); console.log(this == window); }; }; per = new Person(); per.sayHi(); // addr:wy age:18; false setTimeout(per.sayHi,1000); // addr:undefined age:undefined; true
可见,setTimeout在回调func的时候,会把方法的this指向window,所以需要重新绑定对象。
同时还设计传递参数、浏览器版本等问题,详见MDN https://developer.mozilla.org/en-US/docs/Web/API/Window.setTimeout
9. 按照同学的意图,代码应该写成下面的样子,涉及到异步编程的问题,这里忽略不计
// do something 0 (function(){ // do something 1 (function(){ // 写在里面 // do something2 }).defer(1000); }).defer(1000);