JavaScript, перебирающий свойства объекта — застрял в циклическом цикле ссылок

#javascript #loops

#javascript #циклы

Вопрос:

У меня есть большой объект (у него около 200 свойств), и я хочу распечатать его следующим образом:

 [property1: alice, property2: bob, property3: 42, ...]
  

Если свойство является функцией, я хочу, чтобы оно печатало код функции, а если это массив, я хочу, чтобы он печатал каждый элемент этого массива. Кроме того, если свойство является объектом, оно также должно печатать его свойства и так далее…

Я пытался реализовать это рекурсивно, но, конечно, стек вызовов стал слишком большим довольно быстро. Затем я перешел к итеративной реализации с использованием стека. Вот что у меня получилось:

 function getPropertyString(obj) {
    var res = "";
    var stack = [];
    stack.push(obj);
    while(stack.length > 0){
        var object = stack.pop();
        res  = "[";
        for(var prop in object) {
            if(prop == null) continue;
            if(typeof object[prop] === 'object') {
                stack.push(object[prop]);
            } else {
                res  = prop   ": "   
                       object[prop].toString().replace(/[trn]/g, "")   ", ";
            }
        }
        res  = "],";
    }
    return res;
}
  

Это отлично работает, если у вас есть объект, подобный

 var a = {
    b : {
        c : "hello",
        d : "world"
    },
    e : "alice",
    f : "bob",
    g : function() {
        console.log("hello");
    },
    h : [1, 2, 3]
}
  

но, допустим, вы модифицируете a так, чтобы a.x = {}; a.x.prototype = a; . Тогда моя функция застряла бы в бесконечном цикле.

Как я мог обойти это?

Комментарии:

1. Это не имеет ничего общего с цепочкой прототипов (или .prototype свойствами), циклические ссылки всегда могут произойти. (простейший пример: var a = {}; a.x = a; )

2. Спасибо, что указали на это. Должен ли я тогда изменить заголовок?

Ответ №1:

Создайте массив объектов, которые были обработаны из стека, и не обрабатывайте их снова: (Я отметил строки, которые я добавил для этого)

 function getPropertyString(obj) {
    var res = "";
    var stack = [];
    var objectHistory = []; // added this
    stack.push(obj);
    while(stack.length > 0){
        var object = stack.pop();
        if (objectHistory.indexOf(object) != -1) continue;  // added this
        objectHistory.push(object);  // added this
        res  = "[";
        for(var prop in object) {
            if(prop == null) continue;
            if(typeof object[prop] === 'object') {
                stack.push(object[prop]);
            } else {
                res  = prop   ": "  
                       object[prop].toString().replace(/[trn]/g, "")   ", ";
            }
        }
        res  = "],";
    }
    return res;
}

var a = {
    b : {
        c : "hello",
        d : "world"
    },
    e : "alice",
    f : "bob",
    g : function() {
        console.log("hello");
    },
    h : [1, 2, 3]
};

a.x = {
  i: "I am X"
};
a.x.prototype = a;

console.log(getPropertyString(a));  

Комментарии:

1. Просто. Мне это нравится!

2. Всегда приятно порадовать простым решением :-). Если этот ответ работает для вас, пожалуйста, не забудьте пометить его как правильный.

Ответ №2:

Как насчет JSON.stringify того, чтобы сначала передать функцию в качестве 2-го аргумента (так называемый ‘replacer’), чтобы также преобразовать методы в строки, потому что по умолчанию JSON.stringify функции отбрасываются. Таким образом, у вас будут все свойства, и если вы установите «бесконечный цикл», то JSON предупредит вас об этом.

Комментарии:

1. Ну, как я уже сказал, метод работает для объекта a (он также печатает метод и т. Д.). И мне нужно не оповещение, а функция, которая обрабатывает этот случай «цикла».