今天被产品质疑了,关于代码水平!
场景是这样的:上周产品提了个快速需求,在播放页加一个逻辑:用户24小时内第三次及以后访问播放页时,页面中的app下载广告位替换显示为另一个推广广告(影视大全)。
今天产品过来,说大老板给意见了,让把app下载广告替换为影视大全广告的时机改为用户24小时内第6次及以后访问时再进行。
本来是一常规需求,但产品当时提变更的时候,说了一句:是不是你JS这边不用改,直接让cms那边改一下页面就行了?
我回想了一下,好像之前的JS代码就是针对第三次及以后访问进行开发的,因为把最近两次的访问时间给记录到cookie中了。要改成第6次的话,就要记录和检查最近五次的访问时间。之前没考虑到扩展,这次变更还是要先JS这边改逻辑,然后告知页面(这部分JS代码直接贴到页面中了)进行后续更改。所以就如实告诉产品了:要JS这边先改,然后cms那边再改。
但是产品很意外地向我吐槽:不就是改个数吗?怎么还要JS/CMS两边都改呢?
我顿时很无言,只能尴尬地告诉他:我当初逻辑只是按照24小时内第三次及以后访问的需求开发的,没考虑到后续的扩展性,把一些逻辑“硬编码了”,所以现在还是要改的。
现状:硬编码
下面就是之前的24小时内第三次出现的需求场景的JS代码:
var ysdqContainer = Q.$("#widget-ysdq-container"); var appDownContainer = Q.$('#widget-appdown-container'); var ckey = "TQC007"; var cval = Q.cookie.get(ckey);// cval里面记录最近两次打开的时间戳 var DAY = 24 * 60 * 60 * 1000; var copt = { domain: '.iqiyi.com', path: '/', expires: DAY }; var thisVal = +new Date; var showYSDQ = false; var valArr = [], valLen, oldestVal; if(cval){//24小时内第一次打开 Q.cookie.set(ckey, thisVal, copt); }else{//24小时内打开过 valArr = cval.split(","); valLen = valArr.length; oldestVal = parseInt(valArr[valLen - 1], 10);//最早访问的时间戳 if(valLen == 1){//这里硬编码了 valArr.unshift(thisVal - oldestVal); Q.cookie.set(ckey, valArr.join(","), copt); }else{ //下面几行也是hard-coded var recentVal = parseInt(valArr[0], 10);//最近访问的时间戳delta recentVal += oldestVal; //恢复recentVal为时间戳 valArr = [thisVal - recentVal, recentVal]; if(thisVal - oldestVal <= DAY){ showYSDQ = true; } Q.cookie.set(ckey, valArr.join(","), copt); } } if(showYSDQ){ ysdqContainer.show(); appDownContainer.hide(); }else{ appDownContainer.show(); ysdqContainer.hide(); }
其实上面是很普通的代码,没什么大亮点,也就cookie里面最早的访问时间记的是时间戳,后续的访问都是相对于前一次访问时间的增量。
扩展:可配置
下面改成可配置的:
var ysdqContainer = Q.$("#widget-ysdq-container"); var appDownContainer = Q.$('#widget-appdown-container'); var showAfter = ysdqContainer.attr("data-display-after"); if(showAfter){ showAfter = parseInt(showAfter, 10); } var ckey = "TQC007"; var cval = Q.cookie.get(ckey);// cval里面记录最近两次打开的时间戳 var DAY = 24 * 60 * 60 * 1000; var copt = { domain: '.iqiyi.com', path: '/', expires: DAY }; var thisVal = +new Date; var showYSDQ = false; if(!cval){//24小时内第一次打开,以本次访问时间戳为基准时间戳 Q.cookie.set(ckey, thisVal, copt); }else{//24小时内打开过 var valArr = cval.split(","); var valLen = valArr.length; //cookie中最早访问的时间戳,基准时间戳 var oldestVal = parseInt(valArr[valLen - 1], 10); var mostRecentVal = 0; //最近访问的时间戳 for(var i = 0; i < valLen; i++){ //valArr中除了最后一个基准时间戳,其它都是增量时间戳 mostRecentVal += parseInt(valArr[i], 10); } valArr.unshift(thisVal - mostRecentVal);//记录当前访问的增量时间戳 valLen++; if(valLen > showAfter){//cookie中已存够了策略所需次数的访问时间戳记录 var lastOldestVal; //如果策略调整时showAfter变小,比如5次访问之后出现更改为2次访问之后再出现, //那么cookie中最早的3次访问记录要被清理掉,并重设基准时间戳。 while(valLen > showAfter){ lastOldestVal = oldestVal;//记录最早的可用于策略判断的访问时间戳 oldestVal += parseInt(valArr[valLen - 2], 10); valArr[valLen - 2] = oldestVal;//更新基准时间戳 valArr.pop();//丢弃最早的访问记录 valLen--; } if(thisVal - lastOldestVal <= DAY){ showYSDQ = true; } } Q.cookie.set(ckey, valArr.join(","), copt); } if(showYSDQ){ ysdqContainer.show(); appDownContainer.hide(); }else{ ysdqContainer.hide(); appDownContainer.show(); }
优化
补充:除了用一个cookie项记录最近几次的访问时间,还可以用另外一种方式:每次访问都用单独的一个cookie项记录,每个cookie项目的过期时间都设置为24小时。这样只要能读到N个cookie项,就说明24小时内已经访问N次了。
为了节省空间,只用5个cookie项(比如TQC001,TQC002,...,TQC005)进行记录(如果展示逻辑设置为24小时内第6次及以后访问时出现),每个cookie项目的值为1。再用一个cookie项(TQC000)记录最老的cookie项的序号,比如前5次访问时,TQC000的值是1,表示TQC001项是最老的cookie项。第6次访问时,重写TQC001,设置TQC000的值是2;第7次访问时,重写TQC002,设置TQC000的值为3,以此类推,代码如下:
var ysdqContainer = Q.$("#widget-ysdq-container"); var appDownContainer = Q.$('#widget-appdown-container'); var showAfter = ysdqContainer.attr("data-display-after"); if(!showAfter){ return; } showAfter = parseInt(showAfter, 10); var ckey_prefix = "TQC01"; var nextSeq = 1; var lastShowAfter = 0; var meta = Q.cookie.get(ckey_prefix + "0"); if(meta && meta.indexOf(",") > 0){ meta = meta.split(",");//meta里面存储 nextSeq,lastShowAfter nextSeq = parseInt(meta[0]); lastShowAfter = parseInt(meta[1]); } var DAY = 24 * 60 * 60 * 1000; var copt = { domain: '.iqiyi.com', path: '/', expires: DAY }; var counter = 0;//记录24小时内访问的次数 for(var i = 1; i <= lastShowAfter; i++){ if(Q.cookie.get(ckey_prefix + i)){ counter++; } } if( counter < showAfter){//24小时内访问次数没达到showAfter次 ysdqContainer.hide(); appDownContainer.show(); }else{ ysdqContainer.show(); appDownContainer.hide(); } Q.cookie.set(ckey_prefix + nextSeq, 1, copt);//记录本次访问 nextSeq = nextSeq + 1; if(nextSeq > showAfter){ nextSeq = 1; } Q.cookie.set(ckey_prefix + "0", [nextSeq, showAfter].join(","), copt);
第二种方法比第一种更简单,也更节省cookie空间!虽然第一种只占了一个cookie项,但其值中包含时间戳和增量时间戳,其实比showAfter+1个value长度为1的cookie更耗费空间。
TODO,明天验证优化后代码的逻辑正确性。
blog comments powered by Disqus