Search code examples
javascriptarraysdomrecursionecmascript-5

Array.prototype.reduce in recursive function is producing unexpected results


I'm trying write a function to convert an Array to a DocumentFragment. Each item of the array will become an HTMLElement. It's tag name will be the array-item's class name*, and its attributes will be the properties of the array-item whose values are strings.

So for example, if I have these constructors:

function Person(name,pets){
        this.name=name;
        this.pets=pets;
    }
function Pet(name){this.name=name;}

And this data:

var arr=[
        new Person("Bob",[
            new Pet("Sparky"),
            new Pet("Wishbone")
        ]),
        new Person("Mary",[
            new Pet("Maggie"),
            new Pet("Sprinkles")
        ])
    ];

I use this function and it works great:

Array.prototype.toDocFrag=function(){
        return this.reduce(function(docFrag,currentItem){
            elem=document.createElement(currentItem.constructor.name.toLowerCase());
            for (prop in currentItem){
                if (typeof currentItem[prop]==="string") elem.setAttribute(prop,currentItem[prop])
                //if (currentItem[prop] instanceof Array) elem.appendChild(currentItem[prop].toDocFrag())
            }
            docFrag.appendChild(elem)
            return docFrag;
        },document.createDocumentFragment())
    }

If I run arr.toDocFrag(), I get a docFrag with the contents <person name="Bob"></person><person name="Mary"></person> as expected.

But now what I want to do is make it recursive, so that it sees "pets" and appends another DocumentFragment to each <person> so I end up with

<person name="Bob">
    <pet name="Sparky"></pet>
    <pet name="Wishbone"></pet>
</person>
<person name="Mary">
    <pet name="Maggie"></pet>
    <pet name="Sprinkles"></pet>
</person> 

Uncomment out the line I commented out in my code, and I believe that it should work. But for some reason arr.toDocFrag() is returning just <pet name="Wishbone"></pet><pet name="Sprinkles"></pet>

What is wrong with my logic? Am I misunderstanding something about Array.prototype.reduce or recursive functions?

Thanks!


Footnote

*By class name I mean the name of the constructor that initiates the instance.


Solution

  • Your problem is that elem is an implicitly global variable. This doesn't matter for your first function, but completely messes up the recursive function where a call overwrites elem of his caller.

    Use a var statement to declare your variables locally (it's missing for prop as well). And use strict mode to get errors on such behaviour.