2

on a page I use similar syntax to google analitycs code, to pass parameters to another script. I iterate through the array and try to construct query part of URL according to the parameters in the included script.

The problem is the following iterates through the javascript array objects methods also and mess up resulting queryString.

index.html:

<script>
    var params = [];
    params['first'] = '1';
    params['second'] = '2';

    (function() {
          var vs = document.createElement('script');
          vs.async = true; vs.type = 'text/javascript';
          vs.src = 'http://my.domain.com/js/includedScript.js';
          var s = document.getElementsByTagName('script')[0]; s.parentNode.insertBefore(vs, s);
    })();
</script>

includedScript.js:

function(paramsArray) {
   tmpArray = [];
   for(i in paramsArray) {
      tmpArray.push(i + '=' + escape(paramsArray[i]));
   }
   var queryString = tmpArray.join('&');
}(params);

I got (shortened):

queryString == 'first=1&second=2&push&$family=function%20%28%29%20%7B%0A%20%20%20%20return%20lower%3B%0A%7D&$constructor=function%20Array%28%29%20%7B%0A%20%20%20%20%5Bnative%20code%5D%0A%7D&pop=function%20pop%28%29%20%7B%0A%20%20%20%20%5Bnative%20code%5D%0A%7D&push=function%20push%28%29%20%7B%0A%'

I expect:

queryString == 'first=1&second=2'

It's strange, that on my localhost blank page it's working well. Could some other javascript on the index.html page collide with my code? How can I fix the collision by changing only my code (preferably only the includedScript.js file)?

Thanks in advance.

6 Answers 6

5

One small change:

var params = {}; // notice the difference here!
params['first'] = '1';
params['second'] = '2';

and another one here

for(i in paramsArray) {
  if(paramsArray.hasOwnProperty(i)){
    tmpArray.push(i + '=' + escape(paramsArray[i]));
  }
}

JavaScript's Array should only be used with numeric indexes. For associative arrays use Object instead. Also to make sure that the properties you get were added by you and do not belong to the a prototype use hasOwnProperty.

See the documentation on mozilla.org for more info.

An array is an ordered set of values associated with a single variable name. Note that you shouldn't use it as an associative array, use Object instead.

Also you could read this article: http://andrewdupont.net/2006/05/18/javascript-associative-arrays-considered-harmful/


Note: The for ... in JavaScript construct has no connection with Array. Its purpose is to iterate over Object properties, not Array elements. In JavaScript properties also include methods.


Fun with JavaScript.

Try the following code and tell me if you're surprised:

var x = [];
x['one'] = 1;
x['one'] = 2;
alert(x.length);

then try this one:

Object.prototype.myProp = 123;
var x = {one: 1, two: 2}
for(var prop in x){
  alert(x[prop]);
}
Sign up to request clarification or add additional context in comments.

5 Comments

Thanks for this information, browsers didn't throw any errors or warnings so I assumed that the associative arrays work well. Are there any drawbacks if I will continue using the associative arrays with solution of @duncan ?
@Petr Peller I don't think so. But why would you? If you do it like that you only use the Object part of the Array. Note that you can still write $myVar['myProp'] or $myVar['42'] for objects, it's the same thing as using the dot syntax.
Well, if there are no drawbacks why shouldn't I use it? Of coarse I prefer use ideal solution, but here it's much easier to modify includedScript.js instead of index.html since the code inside index.html is on an external page (I don't have access to it)
@Petr Peller Because each thing has it's purpose. Being an associative array is not one of the purposes of an Array object. The article I linked should tell you more about why. If you can't alter the code then your only option is to use a hack, but you should signal that there is a problem and you are using a hack by adding some comments in the code (at least).
@Petr, just for clarification: There is no associative arrays in JavaScript, only Objects. As everything is an object, an array is an object also. Actually, myVar['prop'] (or myVar.prop) will work almost always, no mater if myVar is an Array, a Boolean, a Function, a String... (it won't work if myVar is null or undefined). Of course, as @Alin said, each thing has it's purpose and you should not use Array just to save properties when a regular Object will do the job.
4

You want to do this

for (i in paramsArray) {
    if (paramsArray.hasOwnProperty(i)) {
        ....
    }
}

4 Comments

You can, but you shouldn't want to. It's still wrong to use an Array where an Object should be used. The only reason you can do that is that Array is a child of Object.
Indeed. Petr should both switch to using an object and use the hasOwnProperty method.
Well, if I switch to object (only change [] for {}) I can still use for...in with hasOwnProperty condition and it will work the same? In this case I could continue to use Array on pages where I can't modify the index.html file but use Object on the other pages.
Yes, hasOwnProperty is a function on Objects: developer.mozilla.org/en/JavaScript/Reference/Global_Objects/…
1

You should not use for..in to iterate through array elements. Things can be prototyped onto an object automatically to make use of DOM methods or programatically by other javascript on a page (like prototyping).

You should instead get a count of array elements using .length and a normal for loop.

var l = paramsArray.length;
for (var c = 0; c < l; c++) {
  // do stuff
} 

1 Comment

IMHO, there is a bigger problem using for..in to iterate Arrays: Order is not guaranteed. It usually is, but only by coincidence (I think for..in property order is based on last-access time).
1

duncan's answer is correct, and that is because the for-in loop treats the paramsArray as an object, not an array.

And as Alin mentioned, use for (var i = 0; i < paramsArray.length; i++) for arrays.

Comments

1

This is usually an issue when looping using for...in, which is never really recommended.

A way around it is to use the .hasOwnProperty() method to ensure you are getting your own variables:

for (var k in foo) {
    if (foo.hasOwnProperty(k)) {
       // do something with foo[k]
    }
}

Or as others have suggested, use a regular for loop:

for(var k = foo.lenght-1; k >= 0; --k) {
    // do something with foo[k]
}

Which is more efficient (especially when reversed)

2 Comments

The problem is I need key names to construct the URL query part. I will consider using object instead if the associative arrays will not work well.
@Petr Peller There are no associative arrays in JavaSript! Please understand this fact. There are only Objects that can be used as associative arrays.
1

I love Array.forEach(). It takes a function as an argument. That function receives the element, index, and original list as arguments.

["a","b","c"].forEach(function(item, index, list)
{
     console.log("the item at index " + index + " is " + item);
});

To make it compatible with older versions of IE, see here.

1 Comment

+1 for let me know that something like jQuery.each exist also in pure JavaScript :-)

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.