将DOMWindow对象转换为字符串

10

由于某种原因,我似乎无法在DOMWindow对象上使用JSON.stringify。例如:

console.log(window.self); // Outputs a hierarchical DOMWindow object
console.log(JSON.stringify(window.self)); // Outputs nothing - not even an error

alert(window.self); // Alerts "[object DOMWindow]"
alert(JSON.stringify(window.self)); // Again nothing - not even an error

在Safari和Chrome上进行了测试。有人有什么想法我怎样才能实现这个?

编辑:

将编辑移到一个新问题中,因为它不是特定于此问题的。


3
请参见https://dev59.com/83E95IYBdhLWcg3wdtmj。 - Crescent Fresh
6个回答

3
在Chrome 8 dev中,我收到了“TypeError:Converting circular structure to JSON”(如果您不在框架中,则窗口通常包含自引用的self,window和top引用),因此直接使用JSON.stringify将无法工作。
听起来你正在使用它进行调试输出。如果您只关心某些信息,则可以将该信息复制到一个对象中,然后对其进行字符串化。然后封装在一个函数中,以从window中获取您认为您将永远关心的所有信息。
var data = JSON.stringify({
    'location': window.location
    // etc
});

3
这个答案不是我写的,但我也为了同样的问题而在别处找到了这个答案。感谢@Bergi提供了JSON.stringify深层对象的解决方法。同时,你可以使用https://github.com/Canop/JSON.prune来优化代码。
// JSON.prune : a function to stringify any object without overflow
// two additional optional parameters :
//   - the maximal depth (default : 6)
//   - the maximal length of arrays (default : 50)
// You can also pass an "options" object.
// examples :

//   var json = JSON.prune(window)
//   var arr = Array.apply(0,Array(1000)); var json = JSON.prune(arr, 4, 20)
//   var json = JSON.prune(window.location, {inheritedProperties:true})
// Web site : http://dystroy.org/JSON.prune/
// JSON.prune on github : https://github.com/Canop/JSON.prune
// This was discussed here : https://dev59.com/gGYr5IYBdhLWcg3wNHqh
// The code is based on Douglas Crockford's code : https://github.com/douglascrockford/JSON-js/blob/master/json2.js
// No effort was done to support old browsers. JSON.prune will fail on IE8.
(function () {
    'use strict';

    var DEFAULT_MAX_DEPTH = 6;
    var DEFAULT_ARRAY_MAX_LENGTH = 50;
    var seen; // Same variable used for all stringifications
    var iterator; // either forEachEnumerableOwnProperty, forEachEnumerableProperty or forEachProperty

    // iterates on enumerable own properties (default behavior)
    var forEachEnumerableOwnProperty = function(obj, callback) {
        for (var k in obj) {
            if (Object.prototype.hasOwnProperty.call(obj, k)) callback(k);
        }
    };
    // iterates on enumerable properties
    var forEachEnumerableProperty = function(obj, callback) {
        for (var k in obj) callback(k);
    };
    // iterates on properties, even non enumerable and inherited ones
    // This is dangerous
    var forEachProperty = function(obj, callback, excluded) {
        if (obj==null) return;
        excluded = excluded || {};
        Object.getOwnPropertyNames(obj).forEach(function(k){
            if (!excluded[k]) {
                callback(k);
                excluded[k] = true;
            }
        });
        forEachProperty(Object.getPrototypeOf(obj), callback, excluded);
    };

    Date.prototype.toPrunedJSON = Date.prototype.toJSON;
    String.prototype.toPrunedJSON = String.prototype.toJSON;

    var cx = /[\u0000\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g,
        escapable = /[\\\"\x00-\x1f\x7f-\x9f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g,
        meta = {    // table of character substitutions
            '\b': '\\b',
            '\t': '\\t',
            '\n': '\\n',
            '\f': '\\f',
            '\r': '\\r',
            '"' : '\\"',
            '\\': '\\\\'
        };

    function quote(string) {
        escapable.lastIndex = 0;
        return escapable.test(string) ? '"' + string.replace(escapable, function (a) {
            var c = meta[a];
            return typeof c === 'string'
                ? c
                : '\\u' + ('0000' + a.charCodeAt(0).toString(16)).slice(-4);
        }) + '"' : '"' + string + '"';
    }

    function str(key, holder, depthDecr, arrayMaxLength) {
        var i, k, v, length, partial, value = holder[key];
        if (value && typeof value === 'object' && typeof value.toPrunedJSON === 'function') {
            value = value.toPrunedJSON(key);
        }

        switch (typeof value) {
        case 'string':
            return quote(value);
        case 'number':
            return isFinite(value) ? String(value) : 'null';
        case 'boolean':
        case 'null':
            return String(value);
        case 'object':
            if (!value) {
                return 'null';
            }
            if (depthDecr<=0 || seen.indexOf(value)!==-1) {
                return '"-pruned-"';
            }
            seen.push(value);
            partial = [];
            if (Object.prototype.toString.apply(value) === '[object Array]') {
                length = Math.min(value.length, arrayMaxLength);
                for (i = 0; i < length; i += 1) {
                    partial[i] = str(i, value, depthDecr-1, arrayMaxLength) || 'null';
                }
                return  '[' + partial.join(',') + ']';
            }
            iterator(value, function(k) {
                try {
                    v = str(k, value, depthDecr-1, arrayMaxLength);
                    if (v) partial.push(quote(k) + ':' + v);
                } catch (e) { 
                    // this try/catch due to forbidden accessors on some objects
                }               
            });
            return '{' + partial.join(',') + '}';
        }
    }

    JSON.prune = function (value, depthDecr, arrayMaxLength) {
        if (typeof depthDecr == "object") {
            var options = depthDecr;
            depthDecr = options.depthDecr;
            arrayMaxLength = options.arrayMaxLength;
            iterator = options.iterator || forEachEnumerableOwnProperty;
            if (options.allProperties) iterator = forEachProperty;
            else if (options.inheritedProperties) iterator = forEachEnumerableProperty
        } else {
            iterator = forEachEnumerableOwnProperty;
        }
        seen = [];
        depthDecr = depthDecr || DEFAULT_MAX_DEPTH;
        arrayMaxLength = arrayMaxLength || DEFAULT_ARRAY_MAX_LENGTH;
        return str('', {'': value}, depthDecr, arrayMaxLength);
    };

    JSON.prune.log = function() {
        console.log.apply(console,  Array.prototype.slice.call(arguments).map(function(v){return JSON.parse(JSON.prune(v))}));
    }
    JSON.prune.forEachProperty = forEachProperty; // you might want to also assign it to Object.forEachProperty

}());

这似乎是从这个答案复制的。 - Bergi
1
@Bergi:这篇文章保留了代码注释,这些注释似乎指向了所有相关的来源。看起来这个仓库实际上是基于这个答案构建的。 - BoltClock
Brad,很有可能Bergi链接的答案是你最初获取代码的地方。 - BoltClock
@BoltClock:哦,对了,我没有注意到评论中包括归属。谢谢! - Bergi

3
如其他人所说,stringify不支持循环引用,而DOMWindow包含循环引用。通常情况下,可以使用Douglas Cockford的JSON cycle.js将循环引用转换为JSON。
然而,我刚刚在window上尝试了一下,它仍然会导致堆栈溢出。虽然这可能是cycle.js代码中的一个bug,但更有可能的是window是一个太大的对象。

3

你为什么想要序列化DOM?如果必须这样做,那么你需要查看Crescent的链接。不能将窗口对象进行序列化(字符串化)的原因是它包含循环引用,并且JSON.stringify默认不支持它们。


@dosboy 注意 "你为什么想要这样做"。几乎没有什么好的理由来序列化DOM。 - cregox
@Alex 很长一段时间过去了,你们知道有什么办法可以通过IPC模块发送一个DOM对象吗? - Kragalon
1
无论为什么,OP想要序列化DOM。他们只是想知道如何做到这一点。这个“答案”没有提供任何解决方案(有的话)来解决OP的问题。 - Evan Wieland

2

你没有收到错误提示?我收到了 TypeError: 将循环结构转换为 JSON。我认为,这是不可能的。

另外,windowwindow.self 指向同一个对象(全局对象),所以你不需要使用那个属性...


1
你可以使用这个函数将 window 对象转换为字符串:
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))

不错,这个方法有效!我建议你在“about:blank”页面的控制台中运行它,因为它会将页面内容转换为字符串。这个方法导致了我的浏览器崩溃。或者你可以在一个空白的HTML页面中运行它。 - Lewis Nakao

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