日期:2014-05-16  浏览次数:20326 次

Javascript 那些事 (1)---引子

本文通过解决一个问题,引出javascript的概况

?

问题:? 有两种不同的视图,界面上有个按钮供点击来实现视图间的切换

最直观也简单的想法:

var cur = 1;
function view1(){}
function view2(){}
function control(){
   window['view' + cur](); 
   cur = cur > 1 ? 1 : 2;
}
control();
control();
control();
control();

思考一下,有个全局变量总是不爽, 设想某个函数

function evilF(){
  cur = 0;
}

那么我们的control函数就成了受害者了.? 所以要想法把cur藏起来,试试面向对象如何?因为封装是其特色之一。??

function F(start){  
  this.cur = start;   
}
F.prototype.control = function(){
   window['view' + this.cur](); 
   this.cur = this.cur > 1 ? 1 : 2;
};
var controller = new F(1);
controller.cur = 3; //Oops
controller.control();

顺便做了一个小改进, 用户可以指定从那个页面开始了。结果报错了,问题是我们还能改cur的值。 难道就无计可施了吗? 该闭包闪亮登场了!(It's show time for closure!)

function F(start){
  var obj = {};
  obj.control = function(){   
   window['view' + start](); 
   start = start > 1 ? 1 : 2;
  };
  function P(){}   
  P.prototype = obj;
  return new P();
}

var controller = new F(1);
controller.start = 3;
controller.control();
controller.control();
controller.control = function(){console.log('blahblah');};
controller.control();
delete controller.control; // allow control to shine through from prototype.
controller.control();

万事大吉,除非用户把control给覆盖掉了,这样原型里面的就找不到了,当删掉自定义的之后,一切恢复如初。

换个角度来思考, 这个问题跟什么比较类似呢?日历!

日历的提示,无非是从周一到周日循环复始而已,我们想要的正是类似的功能。我们也可以写一个生成器函数,把这种思想推而广之。

function func_generator(funcs, start){?????????????????????????? ?
?? start = start || 0;
?? funcs = funcs || [];
?? var len = funcs.length;
?? return function () {
??????? if(!len){
?????????? return undefined;
??????? }
??????? if(start >= len){
?????????? start %= len;
??????? }
??????? return funcs[start++] ;
?? };
}

var view_toggler = func_generator([function (){console.log('view1');}, function(){console.log('view2');}], 1);
view_toggler().apply(null,[]);
view_toggler().apply(null,[]);
view_toggler().apply(null,[]);
view_toggler().apply(null,[]);

事情好起来了,除非用户把func_generator 给重写了, 为防止别人无心破环我们的成果, 再多做出一点努力把,把名字放长一点,打个包

var toolkit = (function(){
  return {   
    func_generator: function(funcs, start){                            
     start = start || 0;
     start = start < 0 ? 0 : start; 
    ?funcs = funcs || [];
     var len = funcs.length;
     return function () {
        if(!len){
           return undefined;
        }
        if(start >= len){
           start %= len;
        }
        return funcs[start++] ;
     };  
    }
  };
})();
var view_toggler = toolkit.func_generator([function (){console.log('view1');}, function(){console.log('view2');}], 0);
view_toggler().apply(null,[]);
view_toggler().apply(null,[]);
view_toggler().apply(null,[]);
view_toggler().apply(null,[]);

现在用户把我们的toolkit搞砸的概率大大减小了。

?

再多做一点, 让用户可以自由指定从那里开始切换

var toolkit = (function(){  
  return {     
    func_generator: function(funcs, start){                              
     start = start || 0; 
     start = start < 0 ? 0 : start; 
     funcs = funcs || [];  
     var len = funcs.length;  
     return function () {  
        if(!len){  
           return undefined;  
        }    
        if(arguments.length){
          start = arguments[0];
        }    
        if(start >= len){  
           start %= len;  
        }         
        return funcs[start++] ;  
     };    
    }  
  };  
})();  
var view_toggler = toolkit.func_generator([function (){console.log('view1');}, function(){console.log('view2');}, 
function(){console.log('view3');}, function(){console.log('view4');}], 0);  
view_toggler().apply(null,[]);  
view_toggler().apply(null,[]);  
view_toggler().apply(null,[]);  
view_toggler(0).apply(null,[]);  
view_toggler().apply(null,[]);