Forum > Blogs > Prototype.js Breaks JavaScript Foreach Loops
Prototype.js Breaks JavaScript Foreach Loops
avatar
Country: US
Comments: 6470
News Posts: 413
Joined: 2008-06-21
 
Sun, 11 Jan 2009 23:20:02
0
This site uses the Prototype.js JavaScript library, primarily for the excellent AJAX methods.  But there's a lot more to the library designed to simplify cumbersome methods and make your code work cross-browser without worries.


The developers, though, have gone a bit overboard in places, not letting standards get in their way of imposing their own will on language semantics, including modifying core JavaScript objects.  Adding onto the Array object caused the return of functions with the IN keyword.


In simplistic terms, they broke a keyword (or the keyword was broken, just not fully implemented, per se).


To write a foreach loop (a loop that goes through every element in an array without requiring the knowledge of the key for each element) can be written like this:



for(var key IN array) {

  ...

}



Where key is the array index key for the element for each time through the loop, allowing you to access the element by simply array[key].


Here's an example


<script type="text/javascript">

var array = ['A', 'B', 'C'];

for (var key in array)

{

  document.write(array[key]);

}

</script>



Nice and easy, outputs


ABC


Now let's do nothing to the code at all, but import the Prototype.js file.  Remember, Prototype doesn't execute anything when you load it.



<script type="text/javascript" src="http://thevgpress.com/js/prototype.js"></script>

<script type="text/javascript">

var array = ['A', 'B', 'C'];

for (var key in array)

{

  document.write(array[key]);

}

</script>



Let's check the output:


ABCfunction(iterator,context){var index=0;iterator=iterator.bind(context);try{this._each(function(value){iterator(value,index++)})}catch(e){if(e!=$break)throw e;}return this}function(number,iterator,context){iterator=iterator?iterator.bind(context): Prototype.K;var index=-number,slices=[],array=this.toArray();while((index+=number)=result)result=value});return result}function(iterator,context){iterator=iterator?iterator.bind(context): Prototype.K;var result;this.each(function(value,index){value=iterator(value,index);if(result==undefined||valueb?1:0}).pluck('value')}function(){return[].concat(this)}function(){var iterator=Prototype.K,args=$A(arguments);if(Object.isFunction(args.last()))iterator=args.pop();var collections=[this].concat(args).map($A);return this.map(function(value,index){return iterator(collections.pluck(index))})}function(){return this.length}function(){return'['+this.map(Object.inspect).join(', ')+']'}function(iterator,context){iterator=iterator.bind(context);var result;this.each(function(value,index){if(iterator(value,index)){result=value;throw $break;}});return result}function(iterator,context){iterator=iterator.bind(context);var results=[];this.each(function(value,index){if(iterator(value,index))results.push(value)});return results}function(object){if(Object.isFunction(this.indexOf))if(this.indexOf(object)!=-1)return true;var found=false;this.each(function(value){if(value==object){found=true;throw $break;}});return found}function(){return this.map()}function () { [native code] }function () { [native code] }function(){this.length=0;return this}function(){return this[0]}function(){return this[this.length-1]}function(){return this.select(function(value){return value!=null})}function(){return this.inject([],function(array,value){return array.concat(Object.isArray(value)?value.flatten():[value])})}function(){var values=$A(arguments);return this.select(function(value){return!values.include(value)})}function(){return this.length>1?this:this[0]}function(sorted){return this.inject([],function(array,value,index){if(0==index||(sorted?array.last()!=value:!array.include(value)))array.push(value);return array})}function(array){return this.uniq().findAll(function(item){return array.detect(function(value){return item===value})})}function(){return[].concat(this)}function(){var results=[];this.each(function(object){var value=Object.toJSON(object);if(value!==undefined)results.push(value)});return'['+results.join(', ')+']'}


Not exactly what we want.


There are a couple ways to work around this.  If you want to continue to the use the conventional JavaScript foreach loop using the IN keyword, here's an example of how to dodge the weird Prototype glitch:



<script type="text/javascript">

var array = ['A', 'B', 'C'];

var len = array.length;

var count = 0;

for (var key in array)

{

  if(count >= len)

    break;

  else

    ++count;

  document.write(array[key]);

}

</script>



This doesn't affect the actual length of the array and the functions it returns are at the end, so making sure you only process the correct number of elements eliminates the problem.


Alternatively, you can use the Prototype built-in function for enumerating elements (though this only works on numeric indexes).



<script type="text/javascript">

var array = ['A', 'B', 'C'];

array.each(function(element) {

  document.write(element);

});

</script>



Or, perhaps simplest, setting the array initially to an Object type to wipe out any extension, and hence any weird returns.  This however, is dependent on Object not being extended.



<script type="text/javascript">

var array = new Object();

array = ['A', 'B', 'C'];

for (var key in array)

{

  document.write(array[key]);

}

</script>



Having to work around Prototype defeats its purpose in the first place.

---

Tell me to get back to rewriting this site so it's not horrible on mobile
avatar
Country: CA
Comments: 654
News Posts: 6
Joined: 2008-06-21
 
Tue, 13 Jan 2009 03:23:03
0
I'm going to pretend I understand.

..........

..............


OO AX U

avatar
Country: CA
Comments: 97
News Posts: 1
Joined: 2008-06-25
 
Wed, 14 Jan 2009 21:40:06
0
Is that... code? In the output? What kind of sick monster would do this?!
Log in or Register for free to comment
Recently Spotted:
travo (2m)
Login @ The VG Press
Username:
Password:
Remember me?