Рекурсия дерева окон Javascript и бесконечные объекты

Я пытаюсь написать функцию, которая будет выводить рекурсивное дерево window для всех браузеров. Проблема, о которой я сразу понял, будет связана с бесконечными объектами (window.window.window.window). Просто для смеха, я все равно попробовал, и я получил ошибку, как и ожидал. Uncaught RangeError: Maximum call stack size exceeded (тестирование в Chrome)

Таким образом, первый подход к проверке объектов, которые могли вызвать это, был простым:

if (variable != 'window' && variable != 'top' && variable != 'self' && variable != 'frames')

Я думаю, может быть, это сработало бы, но я просто пропустил пару. Это была хорошая теория, но я все еще получаю максимальную ошибку стека. Поэтому я решил ввести window в консоли Chrome и вручную найти все типы [DOMWindow], чтобы добавить их в этот список. При этом я заметил значение Infinity: Infinity, что привело меня к следующему подходу:

if (typeof namespace[variable]['Infinity'] === 'undefined')

У меня все еще была максимальная ошибка стека с этим, поэтому я немного поискал в Google и узнал о isFinite, так что теперь у меня есть: (редактировать: на самом деле я только что понял, что isFinite это не то, что я думал)

if (isFinite(tree[variable]))

В конце концов ошибка исчезла, но проблема с этим подходом в том, что все объекты в window для этого возвращают false, поэтому рекурсия завершается ошибкой. Я понимаю, что некоторые из подходов, вероятно, даже не совместимы с несколькими браузерами, но было бы неплохо, если бы я мог заставить его работать хотя бы в одном браузере в то же время.

Итак, как я могу проверить объекты, которые вызовут бесконечный цикл?

Вот мой код, просто для тех, кому это может быть интересно:

(function () {
    window.onload = function () {
        window.onload = ''; // don't want to get our own code
        console.log((function (namespace) {
            tree = {};
            for (var variable in namespace) {
                /* gonna need these later
                var variable_typeof = typeof namespace[variable],
                    variable_object_tostring = Object.prototype.toString(namespace[variable]);
                */

                //if (variable != 'window' && variable != 'top' && variable != 'self' && variable != 'frames')
                //if (typeof namespace[variable]['Infinity'] === 'undefined')
                if (isFinite(tree[variable]))
                    tree[variable] = arguments.callee(namespace[variable]);
                else tree[variable] = 'Infinity';
            }
            return tree;
        })(window)); // Start from root
    }
})();

Обновление:
Вот рабочий продукт того, что я наконец придумал, для всех, кто заинтересован.
GGG заслуживает упоминания за его помощь.

function loop (namespace) {
    if (namespace['__infinite_test']) return '[[recursion]]'; // It's infinite
    namespace['__infinite_test'] = true; // Note that we've been through this object
    var tree = {};
    for (var variable in namespace) {
        try { // For an issue in Chrome throwing an error
            namespace[variable]['__tester'] = null;
            delete namespace[variable]['__tester'];
        }
        catch (e) {
            tree[variable] = namespace[variable];
            continue;
        }
        if (namespace.propertyIsEnumerable(variable)) tree[variable] = loop(namespace[variable]);
        else tree[variable] = namespace[variable];
    }
    return tree;
}
console.log(loop(window));

person Shea    schedule 24.02.2012    source источник


Ответы (1)


Один из способов предотвратить бесконечную рекурсию в вашей задаче — отслеживать список всех объектов, которые вы уже посетили, и если вы сталкиваетесь с уже посещенным объектом, вы не возвращаетесь к нему.

Когда вы сталкиваетесь с объектом, которого нет в вашем списке, вы добавляете его в свой список, а затем возвращаетесь к нему.

person jfriend00    schedule 24.02.2012
comment
Я на самом деле пробовал это, но это привело к неполному списку. Я мог бы прибегнуть к этому и найти способ уточнить это. Может быть, я могу сделать отдельную функцию для запуска еще одного уровня и добавить только недостающие элементы. Придется пораскинуть мозгами по этому поводу. - person Shea; 24.02.2012
comment
@andrewjackson - я подозреваю, что тогда в вашей реализации была ошибка. Поделитесь кодом, который вы попробовали, и мы сможем его прокомментировать. - person jfriend00; 24.02.2012
comment
Я его выкинул, надо будет переписать. На самом деле это было первое, что я попробовал, еще до моего так называемого первого подхода. Сейчас я перепишу его, обновлю и дам вам знать, что у меня есть. - person Shea; 24.02.2012
comment
На самом деле, ты прав. Я должен был просто придерживаться первоначального плана, у меня была отличная идея реализовать это, и я думаю, что это сработает отлично. Я опубликую код, когда закончу, если кому-то интересно. Наверное, я просто надеялся на что-то более простое, но я не против сложности. - person Shea; 24.02.2012