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);


结果如下图:

wKiom1LTQGShLHT3AABgSaCxlzs251.jpg


由图可以看出,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方法,他会阻塞浏览器,结果如下图:

wKioL1LTQ3nzlRNVAABvgtEGUQk386.jpg


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中,结果如下图:

wKiom1LTRyWzOGhzAABqhvPTlNw836.jpg


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秒)。


结果如下图:

wKiom1LTSsDjyF3aAABRLu7z50Q944.jpg


小结:浏览器阻止,会导致计时器事件的拖后执行,而不是按照计时器时间执行,也不是不执行。原因,可能跟事件队列有关系,并且虽然拦浏览器被阻止,但是计时器仍然正常计时,时间到的时候,会把事件放入到事件队列。一旦浏览器接触阻止,便会依次执行事件队列的事件。这是个人的想法,以后再考证。


上面说的是阻塞浏览器对计时器的影响,计时器是写在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);