一、问题:

欲将一个JavaScript对象,按如下格式序列化(序列化成字符串):

  • 变量名<类型>: [值]
    • 变量名<类型>: [值]
    • 变量名<类型>: [值]
    • 变量名<类型>: [值]
      • 变量名<类型>: [值]
      • 变量名<类型>: [值]
      • prototype<类型>: [值]
    • prototype<类型>: [值]

二、解决方案:

首先,定义几个辅助方法:

1. duplicate(): 该方法将给定的字符串复制指定的次数后,与原来的字符串相拼接。

/// 
///     复制字符串为原来的n倍
/// 
String.duplicate = function (s, n) {
    var sb = new StringBuffer();
    for (var i = 0; i < n; i++) {
        sb.append(s);
    }
    return sb.toString();
};

2. format(): 该方法按给定的字符串模板与参数列表,合成一个最终的字符串。详细介绍见《给JavaScript的String对象添加一个format方法》:

然后,定义一个辅助类,StringBuffer,用来高效地拼接字符串。

详细介绍见:《JavaScript 版 StringBuffer 类

最后,递归地序列化指定的对象,源码如下:

/// 
///     序列化一个对象(递归地)
/// 
/// 要被序列化的对象
/// 当前正在被序列化的对象在序列化树中的层级(根级为0)
/// 当前正在被序列化的对象的变量名
/// 
///     该方法有四个重载:
///     1. serializeObject(o);
///     2. serializeObject(o, level);
///     3. serializeObject(o, varName);
///     4. serializeObject(o, level, varName);
/// 
function serializeObject(){            
    // 参数列表:
    var o = arguments[0];
    var level = 0;          
    var varName = "";     

    // 重载机制:
    switch(typeof arguments[1]){
        case "number":
            // 重载 2: serializeObject(o, level);
            level = arguments[1];
            if(arguments.length > 2) {
                // 重载 4: serializeObject(o, level, varName);
                varName = arguments[2];
            }
            break;
        case "string":
            // 重载 3: serializeObject(o, varName);
            varName = arguments[1];
            break;
        default:
            // 重载 1: serializeObject(o);
            break;
    }

    var sb = new StringBuffer();
    // 根对象信息:
    sb.appendLine("{0}{1}<{2}>: [{3}]".format(" ".duplicate(level*2), varName, typeof o, o === null ? "null" : o === undefined ? "undefined" : o.toString()));
    // 子对象信息:
    switch(typeof o) {
        case "object":
            for(var i in o){
                //sb.appendLine("{0}{1}<{2}>: [{3}]".format(" ".duplicate((level+1)*2), i, typeof o[i], o[i] === null ? "null" : o[i] === undefined ? "undefined" : o[i].toString()));
                sb.appendLine(this.serializeObject(o[i], level + 1, i));
            } // end for
            break;
        case "undefined":
            break;
        default:
            // 根对象的prototype信息:
            switch(typeof o.prototype){
                case "undefined":
                    break;
                default:
                    sb.appendLine(this.serializeObject(o.prototype, level + 1, "prototype"));
                    break;
            } // end switch (typeof o.prototype)
            break;
    } // end switch (typeof o)
    return sb.toString();
}

完整的代码如下:

; (function () {
    if (typeof zizhujy == "undefined") {
        zizhujy = {};

        window.zizhujy = zizhujy;
    }

    if (typeof zizhujy.com == "undefined") {
        zizhujy.com = {
            /// 
            ///     序列化一个对象(递归地)
            /// 
            /// 要被序列化的对象
            /// 当前正在被序列化的对象在序列化树中的层级(根级为0)
            /// 当前正在被序列化的对象的变量名
            /// 
            ///     该方法有四个重载:
            ///     1. serializeObject(o);
            ///     2. serializeObject(o, level);
            ///     3. serializeObject(o, varName);
            ///     4. serializeObject(o, level, varName);
            /// 
            serializeObject: function(){            
                // 参数列表:
                var o = arguments[0];
                var level = 0;          
                var varName = "";     

                // 重载机制:
                switch(typeof arguments[1]){
                    case "number":
                        // 重载 2: serializeObject(o, level);
                        level = arguments[1];
                        if(arguments.length > 2) {
                            // 重载 4: serializeObject(o, level, varName);
                            varName = arguments[2];
                        }
                        break;
                    case "string":
                        // 重载 3: serializeObject(o, varName);
                        varName = arguments[1];
                        break;
                    default:
                        // 重载 1: serializeObject(o);
                        break;
                }

                var sb = new StringBuffer();
                // 根对象信息:
                sb.appendLine("{0}{1}<{2}>: [{3}]".format(" ".duplicate(level*2), varName, typeof o, o === null ? "null" : o === undefined ? "undefined" : o.toString()));
                // 子对象信息:
                switch(typeof o) {
                    case "object":
                        for(var i in o){
                            //sb.appendLine("{0}{1}<{2}>: [{3}]".format(" ".duplicate((level+1)*2), i, typeof o[i], o[i] === null ? "null" : o[i] === undefined ? "undefined" : o[i].toString()));
                            sb.appendLine(this.serializeObject(o[i], level + 1, i));
                        } // end for
                        break;
                    case "undefined":
                        break;
                    default:
                        // 根对象的prototype信息:
                        switch(typeof o.prototype){
                            case "undefined":
                                break;
                            default:
                                sb.appendLine(this.serializeObject(o.prototype, level + 1, "prototype"));
                                break;
                        } // end switch (typeof o.prototype)
                        break;
                } // end switch (typeof o)
                return sb.toString();
            }
        };

        //
        // String Buffer Class
        //
        function StringBuffer() {
            this.__strings__ = new Array();

            if (typeof StringBuffer._initialized == "undefined") {
                StringBuffer.prototype.append = function (s) {
                    this.__strings__.push(s);
                };

                StringBuffer.prototype.appendLine = function (s) {
                    this.__strings__.push(s + "\n");
                };

                StringBuffer.prototype.toString = function () {
                    return this.__strings__.join("");
                };

                StringBuffer._initialized = true;
            }
        }
        window.StringBuffer = StringBuffer;

        String.prototype.format = function () {
            //        return String.format.apply(arguments);
            var string = this;
            for (var i = 0; i < arguments.length; i++) {
                string = string.replace("{" + i + "}", arguments[i]);
            }

            return string;
        };

        /// 
        ///     复制字符串为原来的n倍
        /// 
        String.prototype.duplicate = function (n) {
            var sb = new StringBuffer();
            for (var i = 0; i < n; i++) {
                sb.append(this);
            }

            return sb.toString();
        };
    }
})();

三、使用示例:

点击这里运行

var o = {
    a: 2,
    b: 3,
    c: {
        a: 2,
        b: 3,
        c: {
            a: 1,
            b: 2,
            c: null,
            d: undefined
        },
        d: function () {
            alert("hello");
        }
    }
};
alert(zizhujy.com.serializeObject(o));

运行结果截图:

在JavaScript练兵场上查看序列化JavaScript对象的效果