description("Check for ... in will properly enumerate elements added or deleted during the loop"); function haveSameProperties(a, b) { var p; for (p in a) { if (!b.hasOwnProperty(p)) return false; } for (p in b) { if (!a.hasOwnProperty(p)) return false; } return true; } function each(o, callback) { var result = {}; for (var property in o) { callback(property, o); if (result.hasOwnProperty(property)) throw "Duplicate property \"" + property + "\" enumerated"; result[property] = 1; } return result; } function testAdd() { var obj = { a : "First" }; obj["m"] = "Second"; obj["z"] = "Third"; var elementsToAdd = [ "c", "t", "k" ]; var addIndex = 0; var result = {}; return each(obj, function(p, o) { if (addIndex < elementsToAdd.length) o[elementsToAdd[addIndex++]] = "Added #" + addIndex; }); } function testDelete() { var obj = { a : "First" }; obj["b"] = "Second"; obj["c"] = "Third"; obj["d"] = "Fourth"; var elementsToDelete = [ "c" ]; var deleteIndex = 0; return each(obj, function(p, o) { if (deleteIndex < elementsToDelete.length) delete o[elementsToDelete[deleteIndex++]]; }); } function testAddDelete() { var obj = { a : "First", b : "Second", c : "Third", j : "Fourth", z : "Fifth", lastOne : "The End" }; elementsToAdd = [ "d", "p" ]; elementsToDelete = [ "z", "lastOne", "c" ]; var loopIndex = 0; return each(obj, function(p, o) { if (loopIndex++ == 1) { for (var i = 0; i < elementsToAdd.length; i++) o[elementsToAdd[i]] = "Added #" + i; for (var i = 0; i < elementsToDelete.length; i++) delete o[elementsToDelete[i]]; } }); } shouldBeTrue("haveSameProperties(testAdd(), { a: 1, m : 1, z : 1 })"); shouldBeTrue("haveSameProperties(testDelete(), { a: 1, b : 1, d : 1 })"); shouldBeTrue("haveSameProperties(testAddDelete(), { a: 1, b : 1, j : 1 })"); for (var i = 0; i < 10000; i++) { if (!haveSameProperties(testAdd(), { a: 1, m : 1, z : 1 })) shouldBeTrue("haveSameProperties(testAdd(), { a: 1, m : 1, z : 1 })"); if (!haveSameProperties(testDelete(), { a: 1, b : 1, d : 1 })) shouldBeTrue("haveSameProperties(testDelete(), { a: 1, b : 1, d : 1 })"); if (!haveSameProperties(testAddDelete(), { a: 1, b : 1, j : 1 })) shouldBeTrue("haveSameProperties(testAddDelete(), { a: 1, b : 1, j : 1 })"); }