是否可以以非递归方式遍历 JavaScript 中的对象

Is it possible to traverse object in JavaScript in non-recursive way?

本文关键字:JavaScript 遍历 对象 方式 递归 是否      更新时间:2023-09-26

例如,我们有一个JavaScript对象,它可以包含具有任意嵌套深度的其他对象。是否可以不使用递归遍历此对象的每个元素?

如果不是,那么数据结构使用非递归迭代使其遍历的最低要求是什么?

正如 SLaks 上面所写的,任何递归都可以表示为带有堆栈的循环。所以经过一段时间的思考,我想出了下一个解决方案:

var myobj = {
    one: "hello",
    two: "world",
    three: {
        one: 1,
        two: 2,
        three: 4,
        four: {
            one: true,
            two: false
        }
    },
    four: "!"
};
function traverse(obj) {
    var stack = [];
    stack.push(obj);
    while (stack.length) {
        for (var j in stack[0]) {
            if (typeof stack[0][j] === 'object') {
                stack.push(stack[0][j]);
            } else {
                console.log('%s: %s', j, stack[0][j]);
            }
        }
        stack.shift();
    }
}
traverse(myobj);

历任意对象需要支持基元类型以及复杂类型(包括数组),以及防止循环引用。下面是一个示例非递归函数,它应该遍历和字符串化任何对象:

function FlatStringify( Arg )
{
   var ToString = '', ArgObject, Resume, nStartIndex, Stack = [], Processed = []
   do
   {
      if( Array.isArray( Arg ) )
      {
         var nIndex, nLen = Arg.length
         if( Resume )
         {
            nStartIndex = Resume[1] + 1
            ArgObject = Resume[2]
            Resume = undefined
            if( nStartIndex < nLen )
            {
               ToString += ', '
            }
         }
         else
         {
            if( Processed.indexOf( ArgObject ? ArgObject : Arg ) >= 0 )
            {
               ToString += '{ <cyclic>'
               nStartIndex = nLen
            }
            else
            {
               Processed.push( ArgObject ? ArgObject : Arg )
               nStartIndex = 0
               ToString += '{'
            }
         }
         nIndex = nStartIndex
         if( nIndex < nLen )
         {
            // Save our Array and loop position
            Stack.push( [ Arg, nIndex, ArgObject ] )
            // Restore Object Context if any!
            if( ArgObject )
            {
               ToString += ' ' + Arg[ nIndex ] + ': '
               Arg = ArgObject[ Arg[ nIndex ] ]
            }
            else
            {
               ToString += ' '
               Arg = Arg[ nIndex ]
            }
            nIndex++
         }
         if( nIndex >= nLen )
         {
            ToString += ' }'
            ArgObject = undefined
         }
         else
         {
            // Skip to the while( ... )
            continue
         }
      }
      else if( typeof Arg === 'object' )
      {
        if( Arg == null )
        {
           ToString += 'null'
        }
        else
        {
           ArgObject = Arg
           Arg = Object.keys( ArgObject )
           continue
        }
     }
     else if( typeof Arg === 'string' )
     {
        ToString += "'" + Arg + "'"
     }
     else if( typeof Arg === 'function' )
     {
        ToString += 'function ' +  Arg.name + '(){...}'
     }
     else if( typeof Arg === 'number' )
     {
        ToString += Arg
     }
     else if( typeof Arg === 'boolean' )
     {
        ToString += Arg
     }
     else
     {
        //console.log( typeof Arg )
        ToString += typeof Arg//String( Arg )
     }
     if( Stack.length )
     {
        //console.log( 'Resuming: ' + Stack.length + '(' + nLoops + ')' )
        Resume = Stack.pop()
        Arg = Resume[0]
     }
   }
   while( Resume || ArgObject || Stack.length )
   return ToString
}