I have defined prototype for Array indexOf ( to support array indexOf in Internet Explorer )
if(!Array.prototype.indexOf){
Array.prototype.indexOf = function(obj){
for(var i=0; i<this.length; i++){
if(this[i]==obj){
return i;
}
}
return -1;
}
}
When I am creating array with values [1,2,3], this indexOf code snippet added into the Array like below
["1","2","3",function(obj){for(var i=0;i<this.length;i++){if(this[i]==obj){return i;}}return -1;}]
This problem happens only in IE.
Can anyone help me to resolve this issue. Thanks in advance.
I didn't use for...in loop anywhere, for this I am using jQuery sortable toArray method .sortable("toArray");.
I am assuming that at some point you are using a for...in loop to iterate over the elements of your array. For example:
for (var elem in myArray) {
//Do stuff...
}
A for...in loop will enumerate all enumerable properties of an object, including those it has inherited from it's ancestors in its prototype chain. You've added a method to the Array prototype:
Array.prototype.indexOf = function(obj){ //...
This property is enumerable (you can't define non-enumerable properties - see Object.defineProperty - in older versions of IE), so a for...in loop will include this property.
The simple solution is to never use a for...in loop to iterate over an array! Use a normal for loop instead.
Your problem is that your new indexOf() method is marked "enumerable." Unfortunately, there is no fix for this in IE7 or non-standards-mode IE8. But you can at least patch the issue for standards mode IE8 by using some ES5 trickery. Modify your code to look like this and it should get rid of the extra element if you're in IE8 standards mode:
(function () {
var indexOfFn = function(obj){
for(var i=0; i<this.length; i++){
if(this[i]==obj){
return i;
}
}
return -1;
};
if(!Array.prototype.indexOf){
if(typeof Object.defineProperty === "function") {
Object.defineProperty(Array.prototype, "indexOf", {
value: indexOfFn,
enumerable: false
});
} else {
Array.prototype.indexOf = indexOfFn;
}
}
}());
I know it's wordy, so it might not be worth the effort. But it will also protect your JavaScript from other people's bad coding where they might end up using an Array with a for-in loop.
It's quite simple, you're array now contains 4 elements, of which the forth is a function object, you're not defining a new method for the array object, let alone all array objects. Just paste the first snippet at the very top of your script, then:
var foo = [1,2,3];
alert(foo.indexOf(2));//alerts 1
Think of Array.prototype as the template of every array. Whenever you try to access some property or method of an array, that isn't defined, rather then throwing errors, JS will first check the Array.prototype if that object doesn't have that method/property. If it does, JS will use that code, and apply it to the array that initially called it. In the above example foo.indexOf(2) could have been written as Array.prototype.indexOf.apply(foo,[2]);. In other words: JS automatically applied the function of the prototype to foo.
Your "full" code should look like this:
if(!Array.prototype.indexOf)
{
Array.prototype.indexOf = function(obj)
{
for(var i=0; i<this.length; i++)
{
if(this[i] === obj)//preferable, use strict comparison
{
return i;
}
}
return -1;
};
}
var yourArray = [1,2,3,4,'4'];
alert(yourArray.indexOf(4));//alerts 3
alert(yourArray.indexOf('4'));//alerts 4 when using strict comparison, if not, alerts 3
Here's a fiddle, checked in IE8, and it's working just fine
Just google prototypal inheritance and prototype chains or augmenting prototypes in JS and the like, read up and be baffled! ;)
Related
I want to define helper methods on the Array.prototype and Object.prototype. My current plan is to do something like:
Array.prototype.find = function(testFun) {
// code to find element in array
};
So that I can do this:
var arr = [1, 2, 3];
var found = arr.find(function(el) { return el > 2; });
It works fine but if I loop over the array in a for in loop the methods appear as values:
for (var prop in arr) { console.log(prop); }
// prints out:
// 1
// 2
// 3
// find
This will screw up anybody else relying on the for in to just show values (especially on Objects). The later versions of javascript have .map and .filter functions built into arrays but those don't show up on for in loops. How can I create more methods like that which won't show up in a for in loop?
It's quite easy: Don't use for-in loops with Arrays. Blame everybody else who does so - here is a nice snippet to tell them during development.
Of course, if one does an enumeration in a generic function and doesn't know whether he gets an array, a plain object or an object with a custom prototype, you can use hasOwnProperty like this:
for (var prop in anyObj )
if (Object.prototype.hasOwnProperty.call(anyObj, prop))
// do something
Notice the explicit use of Object.prototype to get the function - there might be objects that overwrite it (especially in data-maps, the value might not even be a function), objects that do not support it or objects that do not inherit from Object.prototype at all. See also here.
Yet, only a script author who is aware of the problem would filter all his for-in-loops - and some only do it because it gets recommended - and does it mostly wrong, he should have used a for-loop array iteration instead. But our problem are those authors who do not know of it.
An interesting, but Mozilla-only approach would be overwriting the behavior of enumerations on arrays via __iterate__, as demonstrated here.
Fortunately, EcmaScript 5.1 allows us setting properties to be non-enumerable. Of course, this is not supported in older browsers, but why bother? We'd need to use es5-shims anyway for all the cool higher-order array stuff :-) Use defineProperty like this:
Object.defineProperty(Array.prototype, "find", {
enumerable: false,
writable: true,
value: function(testFun) {
// code to find element in array
}
});
Depending on your restrictions:
// In EcmaScript 5 specs and browsers that support it you can use the Object.defineProperty
// to make it not enumerable set the enumerable property to false
Object.defineProperty(Array.prototype, 'find', {
enumerable: false, // this will make it not iterable
get: function(testFun) {
// code to find element in array
};
});
Read more about Object.defineProperty here https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Object/defineProperty
The above answers miss a point:
enumerable ... Defaults to false. (mdn)
So simply using Object.defineProperty(Array.prototype, 'myFunc' myFunc) instead of Array.prototype.myFunc = myFunc will resolve the issue.
It's because have to check for hasOwnProperty:
for (var prop in arr) {
if (arr.hasOwnProperty(prop)) {
console.log(prop)
}
}
Now this logs 1, 2, 3.
Can you explain me this?
JAVASCRIPT
if (typeof Array.prototype.contains != 'function') {
Array.prototype.contains = function (str){
return this.indexOf(str) > -1;
};
}
var text = "Hello world";
var tokens = text.split(" ");
console.log(tokens.length);
for (var i in tokens){
console.log(tokens[i]);
}
OUTPUT
2
"Hello"
"world"
function ()
In other words, the "contains" function i added to the Array prototype appears in the for loop as last element (but the array length is 2). Why?
Why?
Because for-in doesn't loop through array entries, it loops through enumerable properties of an object. You've created a new enumerable property on Array.prototype, and so that property shows up in for-in loops on any array.
The solutions are:
Don't do that, instead loop through arrays in any of several ways that don't have that problem, or
Use hasOwnProperty to filter out inherited properties (see link above for more), or
Define contains as a non-enumerable property via Object.defineProperty or similar (you can only do this on ES5+ engines, it cannot be shimmed).
I've tried to add some new functions to the Array prototype, that I use frequently. My question is how when I add something to the prototype of an Object, and trace out properties in a for-in loop of any new array (object) that I created, those new functions that were added only to the prototype are listed as well? Shouldn't they just be in proto?
Just for example:
So I add an function of "first" to the prototype.
Array.prototype.first = function() { return this[0]; }
So when now I use an for-in loop to iterate over the array, I get the named function, as well as any other items that are in the array.
var array = [1,2,3];
//traces out: 1,3,4,first
for(var i in array) {
console.log(i);
}
Is this something that is solely resulting from the trace and/or use of for-in for iterating over an object?
Evan is correct. However, when using for..in statements in javascript, it is always best to test that the current attribute is a property of the object and not something inherited from the prototype chain:
for(var attr in obj){
if(obj.hasOwnProperty(attr)){
// first will not appear here
}
}
You shouldn't use a for-in loop to loop over an array.
for (var i = 0, len = arr.length; i < len; ++i) {
}
for in is used for iterating the keys of an object.
If you really need to do this, use
Object.defineProperty(Array.prototype, 'first', {
value: function() { return this[0]; },
enumerable: false
});
But some consider this a bad practice. Think twice before doing it.
I don't know how to word this problem exactly but I found this extremely wired.
Basically I did this test in chrome's developer tool console.
for (var request in [0,1,2]) { console.log(request);}
0
1
2
compare
the last four lines are all outputs from the for loop.
during the for loop, request got the value compare.
I wonder if this is a bug in chrome.
for ... in ... iterates over the enumerable properties of an object, and is not intended for array indices. Array indices are also enumerable properties, but as you've discovered anything unsafely added to Array.prototype will be returned too.
To safely add a (non-enumerable) method to Array.prototype in ES5 browsers you can use Object.defineProperty, e.g.:
Object.defineProperty(Array.prototype, 'compare', {
value: function() {
...
}
});
This will stop for ... in from breaking, but it's still the wrong tool for the job when the variable of interest is an array.
You're best off using an indexed for loop.
For..in also enumerates over inherited properties etc.
var request = [0,1,2];
for (var i = 0; i < request.length; i++) {
console.log(request[i]);
}
The top answer to this question:
stackoverflow previous answer
puts it better than I could:
in your case, the global "object-prototype" as a compare function declared for it, e.g...
object.prototype.compare = function (a,b) {return a === b}
...and so, whenever you iterate an object (an array being one kind of object) you also iterate over the "compare" function of it's prototype... which is a "member" of it.
As others pointed out for .. in is not the best way to iterate thru array. If you insist on using it for some reason - use hasOwnProperty method to determine that property indeed belongs to the array:
var arr = [0,1,2];
for (var request in arr ) {
if (arr.hasOwnProperty(request)) console.log(request);
}
I'm seeing something weird when I try to include the ember.js library (ember-1.0.0-rc.7.js).
The javacode I have just prints out a javascript array:
<script type="application/javascript">
var songs = [ 'a','b','c'];
console.debug(songs.toString());
for(key in songs)
{
console.debug(songs[key]);
}
</script>
When I don't include the library, it'll print out a , b, c in the console. However, when i do include it, it start printing out a, b, c, but as well as all the functions to...
Example:
function (idx) {
return this.objectAt(idx);
}
function superWrapper() {
var ret, sup = this._super;
this._super = superFunc || K;
ret = func.apply(this, arguments);
this._super = sup;
return ret;
}
function (key) {
return this.mapProperty(key);
}
Any reason why this occurs with the ember.js library, and how do I resolve this issue?
Any advice appreciated,
Thanks,
D
By default, Ember extends built-in prototypes such as Array.prototype to provide extra methods or shim ES5 methods for non-supporting browsers. You are seeing these methods because for...in iterates over the enumerable properties of an object. These include all properties, even those inherited through the prototype chain.
Instead, you should use a regular for loop to iterate over an array:
for(var i=0; i<songs.length; i++) {
console.debug(songs[i]);
}
This will only ever go over actual array elements, i.e. properties with a numerical key. There are nicer ways though, for example using ES5 Array.forEach (shimmed by Ember in older browsers):
songs.forEach(function(song, i) {
console.debug(song);
});
Optionally, you can disable Ember's prototype extension by configuring Ember.EXTEND_PROPERTIES if you're not planning to use them or if they might conflict with other libraries/scripts. There's a whole page dedicated to this issue in the Ember documentation.
embjer.js must be adding functions to the native Array.prototype. You can check if each key is actually a property on the array itself, and not an inherited property, using hasOwnProperty:
for(key in songs) {
if(songs.hasOwnProperty(key)){
console.debug(songs[key]);
}
}
But, it is usually recommended to always use ordinary for loops with arrays, since they have numerical keys:
for(var i = 0; i < songs.length; i++) {
console.debug(songs[i]);
}
You can also use the forEach function that, ironically, Ember added to the Array prototype if it didn't already exist (in older browsers).
songs.forEach(function(song){
console.debug(song);
});
Demo