这篇文章发布于 2011年10月26日,星期三,22:38,归类于 JS相关。 阅读 98289 次, 今日 1 次 24 条评论
by zhangxinxu from http://www.zhangxinxu.com
本文地址:http://www.zhangxinxu.com/wordpress/?p=1997
一、关于功能情境
先来简单的,页面上一个“兑换礼品”的按钮,这个按钮上有如下些逻辑判断:
1. 用户是否已经登录,如果没有登录,则弹出登录框,让其执行登录操作(无刷新),登录成功后执行2;否则直接执行2.
2. 如果用户已经登录或登录成功,弹出选择礼品数目的弹框,让用户进行数目设置。
上面的逻辑功能该如何实现?
二、现实世界的映射
我们习惯于将现实世界的思维方式映射到程序逻辑的实现中,这是很正常。我们很自然会想到如下的逻辑处理(大家一般都熟悉的jQuery的写法,下同):
var funDLogin = function(callback) { /* ... */}, funDoNumberChange = function() { /* ... */ }; button.click(function() { if (isLogin) { //如果登录,弹框 funDoNumberChange(); } else { //如果非登录,执行登录弹框,成功后执行数目设置弹框 doLogin(function() { //登录成功的回调 funDoNumberChange(); }); } });
现实生活的经验告诉我们,尽量不要走重复的路程,不要做重复的事情,尽量避免从头再来,因为这意味着你付出的辛劳(某种意义上)都白费了。
举简单的例子,我们要去美国,结果兴冲冲赶到机场,发现签证没带,我想谁都不愿理再重新赶回去拿签证。
再举个例子,我们玩网游,比如魔兽,辛辛苦苦打了几个月,好不容易升了几十级,结果号被人盗了,你要重头开始练级,估计是谁都会气得吐血三升而亡的。
上面两个例子可能与主题还不够贴切,再举两个例子吧:
不知大家走迷宫的游戏,如果我们走一条路发现不通,怎么半?是退出到之前一个岔路口重新走呢?还是从起点重新走呢?
美女茜茜认识了个男生,如果这个男生很有钱,茜茜会跟他结婚;如果这个男的现在还是很寒酸,则茜茜可以等两年这个男的有钱了再和他结婚。结果两年后,这个男的果然有钱了,你说茜茜是跟他直接结婚呢,还是要重新认识?
很明显的,按照我们正常的生活经验的思维:迷宫不应该出错了就从头开始走,男的达到女方要求可以直接结婚,而没必要从头开始再走恋爱,熟悉之类的流程了。
在现实世界中,我们的时间总是很宝贵,很有限的,于是,往往总是避免“重头再来”这样子的事情发生(节省时间)。
于是,难免的,我们将这种现实世界的认识映射到程序代码中(如上面代码的逻辑)。这看似OK,例如在随机的N次点击事件中,上面的逻辑所消耗的总时间是最小的,而且看上去也不复杂。但是,如果情况再复杂点……
三、复杂的功能情境
还是那个“兑换礼品”的按钮,现在关联逻辑和判断多了点:
1. 用户是否已经登录,如果没有登录,则弹出登录框,让其执行登录操作(无刷新),登录成功后执行2;否则直接执行2.
2. 如果用户已经登录或登录成功,判断用户是否已经绑定手机,如果用户已经绑定手机,执行3;否则,弹出绑定手机的弹框,有一些绑定的Ajax操作,绑定成功后执行3;
3. 如果用户已经是登录状态,同时手机已经绑定,弹出选择礼品数目的弹框,让用户进行数目设置。
如果我们还是完全按照现实世界的经验去处理上面的功能,则就会是下面这个样子:
var funDoLogin = function(callback) { /* ... */ }, funDoBind = function(callback) { /* ... */ }, funDoNumberChange = function() { /* ... */ }; button.click(function() { if (isLogin) { //如果已经登录 if (isBind) { //如果已经绑定 //则打开数目修改弹框 funDoNumberChange(); } else { //如果未绑定 //打开绑定弹框 funDoBind(function() { //绑定成功的回调 funDoNumberChange(); }); } } else { //如果未登陆,打开登录弹框 funDoLogin(function() { if (isBind) { //如果已经绑定 //则打开数目修改弹框 funDoNumberChange(); } else { //如果未绑定 //打开绑定弹框 funDoBind(function() { //绑定成功的回调 funDoNumberChange(); }); } }); } });
代码的逻辑判断结果出现了几个级别的增加。上面的代码虽然看上去有些啰嗦,但是,基本上每种情况都有一条路可以走到底,很符合我们现实世界的处理,想好出现的各种可能的情况,当问题出现的时候总能从容应对。
好吧,我想不用我说,你也会对上面裹脚布式的code有些意见的——代码冗余啰嗦。现实世界的经验有时候反映在我们程序上就不适合。
四、时间换空间
现实世界我们往往是用空间换时间。但是,在程序的世界里,时间是廉价的,用时间换空间的做法往往更合适。
到底怎么个时间换空间法呢?
很简单,“重头再来”, 具体点就是,碰到什么问题了,解决之,然后从头再来(有别于躲避之,走其他路)。
一有问题就从头再来不是很浪费时间吗?确实,时间损耗要多些,但是,对于目前的计算机而言,你一个眨眼的功夫,CUP不知奔腾了多少下了。这点时间的损耗,我们基本上可以忽略不计。我们应该把重点放在简单清晰的逻辑处理上。
OK,上面的逻辑处理如果使用“重头再来”的策略,该如何实现呢?如下:
var funDoLogin = function(callback) { /* ... */ }, funDoBind = function(callback) { /* ... */ }, funDoNumberChange = function() { /* ... */ }; button.click(function() { if (isLogin && isBind) { //如果登录同时绑定,打开修改礼品数目弹框 funDoNumberChange(); } else { if (!isLogin) { //如果没有登录,打开登录弹框 funDoLogin(function() { //登录后重新触发点击事件 button.trigger("click"); }); } else if (!isBind) { //如果没有绑定,打开绑定弹框 funDoBind(function() { //绑定后重新触发点击事件 button.trigger("click"); }); } } });
上面代码红色高亮注释的部分就是“重头再来”的执行部分。我们浪费了点微不足道的时间,换去了更简单易懂的代码空间。显然,比牺牲空间的做法要划算多了。
五、末了点唠叨
其实时间换空间的做法不仅是在JS中,其他语言也是如此。其实本文内容属于程序算法的一些基础东西,随便卖弄,凑个文章数。已经几个星期没有更新了,因为最近手上有紧急的项目,关键问题是设计的匆忙导致开发的时候出现很多折腾的问题,最近又要折腾手机版的开发。
末了的随意唠叨,见谅。我JS其实还是比较菜的,文中有什么表述不准确的地方欢迎指正。感谢阅读。
本文为原创文章,转载请注明来自张鑫旭-鑫空间-鑫生活[http://www.zhangxinxu.com]
本文地址:http://www.zhangxinxu.com/wordpress/?p=1997
(本篇完)
- 最近整的MooTools库下Mbox弹框插件 (0.666)
- zSlide-基于CSS3/HTML5演示文档jQuery插件 (0.666)
- CSS相对定位|绝对定位(五)之z-index篇 (0.598)
- 小tip: margin:auto实现绝对定位元素的水平垂直居中 (0.598)
- 实力科普:为什么浮层或弹框一定要有叉叉关闭按钮? (0.598)
- 聊聊Top Layer顶层特性的隐患与实践 (0.598)
- 回流与重绘:CSS性能让JavaScript变慢? (0.334)
- 翻译 - CSS Sprites:实用技术还是生厌之物? (0.279)
- 翻译-不同CSS技术及其CSS性能 (0.279)
- 实验:absolute/display隐藏与回流等性能 (0.279)
- jquery之append与insertBefore使用实例 (RANDOM - 0.069)
看了这篇文章,我觉得我好想理解一个问题了,在开发的时候不是说你写的快就是最好的,而是你思考的全面才是最好的。
废话一大推
傻逼
不错,原来还可以这么想,受教了
取反判断、trigger使用、程序时间换取开发的成本,很赞!
有同事推荐,开始看楼主的博客,楼主的问题感觉可以用promise的方式解决
checkLogin().then(checkBind(), err()).then(doNumberChange(),err());
checkLogin(){
var d = new Deferred();
已登录:d.resolve();
未登录:doLogon(),然后d.resolve();
}
checkLogin(){
var d = new Deferred();
已绑定:d.resolve();
未绑定:doBind(),然后d.resolve();
}
这样将每个函数的职责分离,有不对还请博主不吝指正
为什么“四、时间换空间”中需要“button.trigger(“click”); ”,而“三、复杂的功能情境”中却不需要呢?是不是遗漏了?
每日一读
赞同 有时候往往没必要为了几毫秒的时间 来增加几十B的代码量
受教了。
真的很喜欢你的文章,,,真的让我学到很多东西,,
嗯,这样真的挺好啊,简化了逻辑与实现。
但是我真的需要花点时间说服自己浪费的那点时间是微不道的。。。
可以这样写,把依赖抽象出来
doCheck([needLogin, needBind], function(){
funDoNumberChange();
});
受用了
兄弟们如果有用不完的精力,可以尝试去赚点美刀。没有开玩笑的意思。
http://www.pokki.com/contest/
这个在我以前写的一些程序里面用过
的确代码的清晰节约的是现实时间,程序的那些时间可以忽略不计
这篇文章很不错!!谢谢
楼主以后能多发表一些手机开发方面的文章就更好了
这就是递归吧。
function test(i) {
if(i>0) {
console.log(i);
test(–i);
}
}
test(20);
button.click(function() {
if (!isLogin) {
//如果没有登录,打开登录弹框
funDoLogin(function() {
//登录后重新触发点击事件
button.trigger(“click”);
});
return;
};
if (!isBind) {
//如果没有绑定,打开绑定弹框
funDoBind(function() {
//绑定后重新触发点击事件
button.trigger(“click”);
});
return;
};
funDoNumberChange();
}
我跟你的想法一样,我觉得这个更好理解一些
也很不错
if..else..采用取反判断出不符合要求的做法,先解决了,就不用if else层层嵌套了
好想法