In JavaScript, object and array are passed by reference.
Sometimes you may come into trouble if you don’t keep this in mind, especial in
recursion.
Let’s see an example:
First, prepare a JavaScript array to present a tree data:
var treeData = [{ "name": "root1", "kids": [{ "name": "leaf1" }, { "name": "leaf2"}] }, { "name": "root2"}];
The expected html structure is:
<ul> <li>root1 <ul> <li>leaf1</li> <li>leaf2</li> </ul> </li> <li>root2</li> </ul>
Our JavaScript code:
function resolveTree(rootNode, kids) { var ulNode = $("<ul />").appendTo(rootNode); $.each(kids, function(i, item) { var liNode = $("<li />").html(item.name).appendTo(ulNode); if (item.kids) { resolveTree(liNode, item.kids); } }); } resolveTree($("#tree"), treeData);
Next, we want to store the tree node’s path.
For example, leaf1 - [root1, leaf1], leaf2 - [root1, leaf2], root2 - [root2]
We modifiy the resolveTree function:
function resolveTree(rootNode, kids, path) { var ulNode = $("<ul />").appendTo(rootNode); $.each(kids, function(i, item) { var liNode = $("<li />").html(item.name).appendTo(ulNode); path.push(item.name); liNode.data("path", path).click(function(event) { event.stopPropagation(); alert($(this).data("path")); }); if (item.kids) { resolveTree(liNode, item.kids, path); } path.pop(); }); } resolveTree($("#tree"), treeData, []);
The logic must be right, but the result is suprising.
No matter which node i click, the alert box contains an empty string.
What’s the matter?
oh… the array is passed by reference….
The variable path in the code - liNode.data(”path”, path) - is a reference. Then
we want a clone of the array.
There are many method to create a array clone.
// Method 1 var newArray = []; for (var i = 0; i < oldArray.length; i++) { newArray.push(oldArray[i]); } // Method 2 var newArray = oldArray.slice(0); // Method 3 var newArray = [].concat(oldArray);
The final solution:
$(function() { var treeData = [{ "name": "root1", "kids": [{ "name": "leaf1" }, { "name": "leaf2"}] }, { "name": "root2"}]; function resolveTree(rootNode, kids, path) { var ulNode = $("<ul />").appendTo(rootNode); $.each(kids, function(i, item) { var liNode = $("<li />").html(item.name).appendTo(ulNode); path.push(item.name); liNode.data("path", [].concat(path)).click(function(event) { event.stopPropagation(); alert($(this).data("path")); }); if (item.kids) { resolveTree(liNode, item.kids, path); } path.pop(); }); } resolveTree($("#tree"), treeData, []); });