序列化JavaScript的navigator对象

17

我正在创建一个页面,帮助诊断我们的用户在网页上遇到的问题(你知道,询问用户“您使用什么浏览器?”通常会得到“互联网”)。

这个页面已经将所有HTTP头提交给了我,现在我想让JavaScript提供更多信息,所以我想获取用户的navigator JavaScript对象,并尝试对它进行序列化,以便可以通过表单进行提交。

问题是我无法使用我知道的任何JSON库对navigator对象进行序列化,每个库都返回一个空对象(?!),因此我决定编写一个特定用途的序列化程序。

你可以在这里找到代码:

<!DOCTYPE html>
<html>
    <head>
        <script src="https://ajax.googleapis.com/ajax/libs/jquery/1.5.0/jquery.min.js" type="text/javascript"></script>
        <script type="text/javascript">
            function serialize (object) {
                var type = typeof object;
                if (object === null) {
                    return '"nullValue"';
                }
                if (type == 'string' || type === 'number' || type === 'boolean') {
                    return '"' + object + '"';
                }
                else if (type === 'function') {
                    return '"functionValue"';
                }
                else if (type === 'object') {
                    var output = '{';
                    for (var item in object) {
                        if (item !== 'enabledPlugin') {
                            output += '"' + item + '":' + serialize(object[item]) + ',';
                        }
                    }
                    return output.replace(/\,$/, '') + '}';
                }
                else if (type === 'undefined') {
                    return '"undefinedError"';
                }
                else {
                    return '"unknownTypeError"';
                }
            };
            $(document).ready(function () {
                $('#navigator').text(serialize(navigator));
            });
        </script>
        <style type="text/css">
            #navigator {
                font-family: monospaced;
            }
        </style>
        <title>Serialize</title>
    </head>
    <body>
        <h1>Serialize</h1>
        <p id="navigator"></p>
    </body>
</html>

这段代码在Firefox、Opera、Chrome和Safari中似乎完美运行,但在Internet Explorer(至少8.0版本)中无法工作,它会在for (var item in object) {这一行报错:"对象不支持此属性或方法"。

您有没有什么提示可以修复代码或通过其他方式实现目标(序列化navigator对象)?


解决方案(v 2.0):

替换为

for (var item in object) {
    if (item !== 'enabledPlugin') {
        output += '"' + item + '":' + serialize(object[item]) + ',';
    }
}

使用

for (var item in object) {
    try {
        if (item !== 'enabledPlugin') {
            output += '"' + item + '":' + serialize(object[item]) + ',';
        }
    }
    catch (e) {
    }
}

它能够正常工作。


$('#navigator').serialize(navigator) 或者 $('#navigator').text().serialize(navigator)?我是jquery新手,但这看起来符合API。 - krs1
@krs1:jQuery的$("element").serialize();仅适用于表单元素(请参阅.serialize())。 - Albireo
我简直不敢相信我还没有找到一个jQuery插件来解决这个常见问题,甚至连一个可以安全地将对象转换为字符串的插件都没有。 - Michael Scheper
3个回答

20
尝试将它放到一个新对象中。
var _navigator = {};
for (var i in navigator) _navigator[i] = navigator[i];

然后对它进行序列化(如果浏览器没有原生的 JSON API,可以使用某些 JSON 库,我使用 json2.js):

$('#navigator').text(JSON.stringify(_navigator));

编辑:看起来Internet Explorer不允许迭代navigator.pluginsnavigator.mimeTypes,因此这个方法可行:


var _navigator = {};
for (var i in navigator) _navigator[i] = navigator[i];

delete _navigator.plugins;
delete _navigator.mimeTypes;

$('#navigator').text(JSON.stringify(_navigator));

但是在Internet Explorer中这样做不会起作用,因为您正在使用“for(var item in collection)”结构,而该结构不受IE支持。 - Albireo
我认为IE没有报告错误的行号,即使开发者工具栏也会突出显示该行,并且网络上的资源表明它不受支持。您是否尝试过我的代码或简单循环?您正在运行哪个版本的IE? - Albireo
没有提到 for (var item in collection) 结构,我已经使用它很长时间了,也没有发现问题。这可能是指新的 for each (var item in iterator)Array.prototype.forEach - Thai
当浏览器是Internet Explorer时,跳过mimeTypes属性似乎可以工作(我保留了plugins属性,它似乎可以工作,即使它报告 "plugins":{"length":"0"},")。 - Albireo
更新:plugins 在IE8中可以工作,但在早期版本中无法工作,因此也必须跳过。 - Albireo
显示剩余4条评论

13

接受的答案中JSON仅包含顶级元素。请查看https://jsfiddle.net/j1zb7qm0/ - _navigator.connection为空。我编写了一个小函数来收集所有嵌套属性:

function recur(obj) {
  var result = {}, _tmp;
  for (var i in obj) {
    // enabledPlugin is too nested, also skip functions
    if (i === 'enabledPlugin' || typeof obj[i] === 'function') {
        continue;
    } else if (typeof obj[i] === 'object') {
        // get props recursively
        _tmp = recur(obj[i]);
        // if object is not {}
        if (Object.keys(_tmp).length) {
            result[i] = _tmp;
        }
    } else {
        // string, number or boolean
        result[i] = obj[i];
    }
  }
  return result;
}

你可以像这样使用它:var _navigator = recur(navigator) 或创建自己的包装器。事实上,你可以使用它来迭代并复制任何嵌套对象。


我正在使用您的代码,并且它完全符合我的要求,但是我遇到了问题。当我尝试使用 var _navigator = recur(window) 时,会显示错误 Uncaught RangeError: Maximum call stack size exceeded。我该如何避免这个错误? - Anas Fanani

0
我已经更新@vladkras的答案,使该函数可以处理嵌套和循环对象。
function recur(obj, visited = new WeakSet()) {
    if (visited.has(obj)) {
        return {}; // skip already visited object to prevent cycles
    }
    visited.add(obj); // add the current object to the visited set

    var result = {}, _tmp;
    for (var i in obj) {
        try {
            // enabledPlugin is too nested, also skip functions
            if (i === 'enabledPlugin' || typeof obj[i] === 'function') {
                continue;
            } else if (typeof obj[i] === 'object') {
                // get props recursively
                _tmp = recur(obj[i], visited);
                // if object is not {}
                if (Object.keys(_tmp).length) {
                    result[i] = _tmp;
                }
            } else {
                // string, number or boolean
                result[i] = obj[i];
            }
        } catch (error) {
            // handle error, you can log it here if needed
            // console.error('Error:', error);
        }
    }
    return result;
}
JSON.stringify(recur(window))

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接