本文通过解决一个问题,引出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,[]); view_toggler().apply(null,[]); view_toggler().apply(null,[]); view_toggler().apply(null,[]);