看了 InfoQ 的代码之丑专栏,心痒痒忍不住,前端也来一个系列吧。

分析的代码片段大都来自实际项目。还希望代码主人多多包容,让我们一起共同研究探讨,互助学习提高。

实际代码

今天要分析的是获取邮费目的地的一段代码(做了部分简化,整体保持原貌):

var DEST_MAP = {
    '1': '全国',
    '110000': '北京',
    '230000': '黑龙江'
};
// 注:精简了数据。真实代码中,有 36 个键值对,表示全国各个省份。
var DEST_MAP_UTF8 = {
    '1': '全國',
    '110000': '北京',
    '230000': '黑龍江'
};
var doc = document, EMPTY = '';
var charset = doc.charset || doc['characterSet'] || 'gb2312';
var isUTF8 = charset.toLowerCase().indexOf('utf-8') != -1;
var getDestNameByCode = (function() {
    if (isUTF8) {
        return function(destCode) {
            return DEST_MAP_UTF8[destCode] || EMPTY;
        }
    }
    else {
        return function(destCode) {
            return DEST_MAP[destCode] || EMPTY;
        }
    }
})();

淘宝的页面,有简体版和繁体版,简体版采用 GB 编码,繁体版用 UTF-8 编码。上面的代码,根据 charset 来判断是否繁体版。然后根据是否繁体版,来决定getDestNameByCode的定义。

冗余闭包

在 JavaScript 里,可以用闭包来封存数据。上面的代码,闭包中并没有需要封存的数据。很显然可以去掉闭包,用条件表达式来实现分支化即可:

var getDestNameByCode = isUTF8 ?
    function(destCode) {
        return DEST_MAP_UTF8[destCode] || EMPTY;
    } :
    function(destCode) {
        return DEST_MAP[destCode] || EMPTY;
    };

去掉一层冗余闭包,胃里舒服多了,但上面的代码依旧让人感觉很雷同。两个分支函数里,只有DEST_MAP不同。进一步优化:

var MAP = isUTF8 ? DEST_MAP_UTF8 : DEST_MAP;
function getDestNameByCode(destCode) {
    return MAP[destCode] || EMPTY;
}

差异化数据

代码越来越少,可阅读性也没降低,心情逐渐愉快起来。但一看到DEST_MAP和DEST_MAP_UTF8两大段数据,心情还是糟糟的。这两个数据对象,key 值是一样的,只有部分 val 值有繁简区别。很快想到下面的改进方式:

var doc = document,
    charset = doc.charset || doc['characterSet'] || 'gb2312',
    DEST_MAP = {
    '1': '全国',
    '110000': '北京',
    '230000': '黑龙江'
};
if (charset.toLowerCase() === 'utf-8') {
    DEST_MAP['1'] = '全國';
    DEST_MAP['230000'] = '黑龍江';
}
function getDestNameByCode(destCode) {
    return DEST_MAP[destCode] || '';
}

doc和charset变量在其它代码中还需要用到,保留定义。isUTF8仅用来分支化繁简数据,直接内联化处理。

上面的代码,和原始代码相比,代码量减少了不少,可阅读性也还不错,很清晰。这样折腾后,胃里舒服多了,哈哈。

过犹不及

代码优化,要时刻权衡性能与可维护性,权衡时间和空间。从空间占用上讲,上面的数据对象还可以进一步缩减:

var DEST_MAP = {
    '1': ['全国', '全國'],
    '110000': '北京',
    '230000': ['黑龙江', '黑龍江']
};

然后在getDestNameByCode方法里,根据isUTF8决定获取哪一个值。这样能让整体代码更小,但增加了getDestNameByCode的复杂性,可阅读性也降低了,感觉过犹不及。

结语

好了,第一期前端代码之丑结束。各位朋友,谈谈你的想法?