二维数组转JSON格式字符串的思考

上周有一个同事求一函数,作用是将一个二维数组转换成JSON格式的字符串,其数据结构固定为数组-数组-整数,即要求的函数输入与输出如下:

var array = [[10, 100, 1000], [1000, 100, 10]];  
var s = encode(array);  
assert(s === '[[10,100,1000],[1000,100,10]]');  

函数的实现本身不难,但结果经过1小时的思考,函数有了好几个形态。

基本模式

首先是一个基本的将数组转成字符串的函数,由于数组中只有可能出现ArrayNumber两种类型,因此不需要额外处理DateFunctionRegExpString等类型。

function encodeArray(obj) {  
    var array = [];
    var isArray = Object.prototype.toString.call(obj) == '[object Array]';
    if (isArray) {
        for (var i = 0; i < obj.length; i++) {
            array[array.length] = encode(obj[i]);
        }
        return '[' + array.join(',') + ']';
    }
    else {
        return obj + '';
    }
}

这个函数工作的很好,但显而易见的是,递归和类型判断不断消耗着CPU,在有可能明确数组中所有对象类型为Number时也使用for循环进行拼装,该函数效率之低下,惨不忍睹。

以数据结构固定为前提

其实反观最初的需求,数据格式是相当固定的,一个数组中放另一些数组,第二维的数组中存放数字,因此以固定的数据结构为前提,函数可以重构成如下模式。

function encodeArray2D(obj) {  
    var array = [];
    for (var i = 0; i < obj.length; i++) {
        array[i] = '[' + obj[i].join(',') + ']';
    }
    return '[' + array.join(',') + ']';
}

作为对比,将encodeArrayencodeArray2D各运行10000次,通过Firebug console查看时间消耗,结果如下:

函数 运行时间
encodeArray 180ms
encodeArray2D 41ms

速度上相差4.5倍,从这一点上看,效率已经得到了非常大的提升。

寻求原生API支持

最近一些浏览器都提供了原生的JSON对象支持,包括以下浏览器:

  • Mozilla Firefox 3.5+
  • Microsoft Internet Explorer 8+
  • Opera 10.5+
  • Webkit-based browsers

在原生JSON对象支持的情况下,只需要JSON.stringify(obj);即可实现该功能,因此函数可以进一步修改为如下模式。

var encodeArrayNative = (function() {  
    var JSON = window.JSON;
    var useNative = JSON && JSON.toString() == '[object JSON]';

    if (useNative) {
        return function(obj) {
            return JSON.stringify(obj);
        };
    }
    else {
        return encodeArray2D;
    }
}());

在支持原生JSON的各大浏览器中运行10000次encodeArrayNativeencodeArray2D,对消耗的时间进行比较,有以下结果:

浏览器 encodeArray2D
IE 8 79ms 36ms
Firefox 3.6 36ms 34ms
Chrome 7 17ms 34ms

Chrome在此做出了古怪的行为,使用原生JSON的效率竟然还不如encodeArray2D函数。反观encodeArray2D的实现,估计V8引擎在Arrayjoin函数上优化非常好,导致原生JSON败给了encodeArray2D

总结

  • 在需求明确,结构固定的前提下,写通用的函数是非常吃力不讨好的。
  • 不要迷信native function,有时候你可以做得更好。
  • 但也不要随意挑战native function,那几乎是找死。

函数及测试脚本、结果可点此查看

文章的测试数据已经是相当陈旧了,现在Chrome下原生的JSON.stringify函数性能早就全面超越自定义的函数了。