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, []);
});