更好的共用化封装是程序员不断追求的目标

2017-01-11 15:24:32来源:segmentfault作者:夜影森林人点击

程序员总是在做重复性的工作,常常因为80%公用的内容,但有20%的不同之处,导致要重写,或复制修改;


更好的共用化封装是程序员不断追求的目标,设计的公用性与适用度还有效率之间要找平衡点;


举些例子,分享给新手!


1. 附加功能封包

如通常做优化时,需要计算代码运行时间,工具有时不能很细致也受到环境限制,所以不可避免需要写一些计时的脚本调试或统计:


/**
* 生成一个计算函数运行时间的新函数(可以打印或使用回调函数返回运行时间值)
*
* 新函数与原函数调用及功能不变;
*
* @param {function} cb测试函数
* @param {object} [thisObj=cb.this]测试函数的 this,如果参数为 undefined 时 this 为函数内部绑定的this
* @param {function} [timeCb] 用来获取运行时间回调函数
* @param {number} [timeCb.time] 运行时间 ms
* @param {number} [timeCb.cb] 原函数
* @param {...} [timeCb.args] 多参数,传入原函数的参数
* @return {function} 返回cb原功能函数增加了计算功能后封包的函数
*/
function runTime(cb, thisObj, timeCb) {
var t;
return function () {
var args = Array.prototype.slice.call(arguments);
t = Date.now();
if( thisObj===undefined ) thisObj = this;
var ret = cb.apply(thisObj, args);
var v = Date.now()-t;
if( timeCb ) {
timeCb.apply(null, [v, cb].concat(args));
} else {
console.log(cb.name, 'runTime:', v, 'ms');
}
return ret;
};
};

调用:

function sum(n) {
var m = 0;
for (var i = 0; i < n; i++) {
m+=i;
}
return m;
}
function getRunTime(time) {
if( time<1000 ) {
console.log('运行时间1秒内可以接受', time);
} else {
console.warn('运行时间超过了1秒不能接受', time);
}
} var sum2 = runTime(sum);
// 临时调试打印结果
console.log(sum2(100000000));
// 需要获取时间处理相关逻辑
var sum3 = runTime(sum, null, getRunTime);
sum3(100000000);
sum3(5000000000);
// 如果不想在生产时使用,可以设计运行条件,或在gulp生成时排除
sum4 = window.isDebug ? runTime(sum) : sum;
sum4(100000000);
2. 通用的动态处理

通常获取数据属性都是静态的,也就是 o.x 类似这样,但当数据有一定不确定性时,如 o.point.x 这时如果 o.point 有可能为 undefined 时,就会报错Cannot read property 'x' of undefined;


有人可能会问,为什么不将数据构造好,避免这样的情况呢!或在发现问题补上数据即可...


上面说的是静态数据的处理方式,如系统配置数据。动态数据的区别是数据的变动性,可能是用户操作构造,可能是服务端查询数据返回,可能是本地缓存需要反复更新修改的数据等等。


动态数据会产生较多结构设计固定,但实际内容不确定,或一定要写成固定式的就会增加很多结构维护消耗。如:


var users = {
user1: {
info: {
hobby: ['玩游戏', '蓝球', '看电影']
},
// ...
},
user2: {
info: {
hobby: []
},
// ...
},
user3: {}
// ...
};

如果业务需求要将所有没有填写爱好的用户收集起来,常规判断方法是:


(试想一下,再增加些写数据的需求,复杂度就更高了,不仅需要判断对象存在性,还要一层层创建数据,如将填写了爱好的用户增加系统动态评分等等)


var ret = [];
for( var k in users ) {
var user = users[k];
if( user && user.info && user.info.hobby ) {
var fristHobby = user.info.hobby[0];
if( fristHobby ) ret.push(fristHobby);
}
}
console.log(ret);

如果你习惯这样的多重判断,反复出现或在不同业务逻辑中返回硬性封装而不感到厌烦,那就可以跳过此文。


实际我们想一次能拿到属性,理想调用如下:


var ret = [];
for( var k in users ) {
var fristHobby = attr(users, k + '.info.hobby[0]');
if( fristHobby ) ret.push(fristHobby);
}
console.log(ret);

代码实现:

/**
* 获取或设置对象属性,允许多级自动创建
*
* @param {object} obj 需要属性的对象,通常操作JSON
* @param {String} name 属性名称(.[]是关键字不可以命名),可以多级,如:name 或 msg.title 或 msg.list[0].user
* @param value 赋值
* @return 返回对象属性,未找到返回 undefined
*/
function attr(obj, name, value) {
if (!obj || typeof obj != 'object') return undefined;
var pAry = name.split('.');
var key = null;
var isSet = (arguments.length > 2);
for (var i = 0; i < pAry.length; i++) {
key = pAry[i].trim();
// 如果此键为数组,先要指向数组元素
if (/^[^/[]+/[/d+/]$/.test(key)) {
var tempAry = key.split(/[/[/]]/g);
key = tempAry[0];
if (!obj[key]) {
if (!isSet) return undefined;
obj[key] = [];
}
obj = obj[key];
key = parseInt(tempAry[1]);
}
// 然后判断是否最后一级,则直接赋值 结束
if (i >= pAry.length - 1) {
if (!isSet) return obj[key];
obj[key] = value;
break;
}
// 否则还有下级,则指向下级对象
if (!obj[key] || typeof obj[key] != 'object') {
if (!isSet) return undefined;
obj[key] = {};
}
obj = obj[key];
}
return value;
};

使用这个封装完成用户增加系统动态评分就简单了,此方法支持多级写数据:


var ret = [];
for( var k in users ) {
var n = 0;var fristHobby = attr(users, k + '.info.hobby[0]');
if( fristHobby ) {
ret.push(fristHobby);
n++;
}var score = attr(users, k + '.info.score');
attr(users, k + '.info.score', score ? score + n : n);
}
console.log(ret, users);
更多例子

未完待续...

最新文章

123

最新摄影

微信扫一扫

第七城市微信公众平台