I know this is an old thread, but maybe there is still some relevance in it?
Inspired by Jacky Li's good solution I tried a slight variation of my own with the objective to also be able to take care of arbitrary combinations of arrays and objects as input. I looked at how PHP would have done it and tried to get something "similar" going. Here is my code:
function getargs(str){
   var ret={};
   function build(urlnam,urlval,obj){ // extend the return object ...
    var i,k,o=obj, x, rx=/\[([^\]]*)\]/g, idx=[urlnam.replace(rx,'')];
    while (x=rx.exec(urlnam)) idx.push(x[1]); 
    while(true){
     k=idx.shift();
     if(k.trim()=='') {// key is empty: autoincremented index
       if (o.constructor.name=='Array') k=o.length; // for Array
       else if (o===obj ) {k=null}  // for first level property name
       else {k=-1;                                  // for Object
         for(i in o) if (+i>k) k=+i;
         k++;
       }
     }
     if(idx.length) { 
       // set up an array if the next key (idx[0]) appears to be
       // numeric or empty, otherwise set up an object:
       if (o[k]==null || typeof o[k]!='object') o[k]=isNaN(idx[0])?{}:[]; 
       o=o[k]; // move on to the next level
     }
     else { // OK, time to store the urlval in its chosen place ...
       // console.log('key',k,'val',urlval);                 
       o[k]=urlval===""?null:urlval; break; // ... and leave the while loop.
     } 
    }
    return obj;
   }
   // ncnvt: is a flag that governs the conversion of
   // numeric strings into numbers
   var ncnvt=true,i,k,p,v,argarr=[],
       ar=(str||window.location.search.substring(1)).split("&"),
       l=ar.length;
   for (i=0;i<l;i++) {if (ar[i]==="") continue;
     p=ar[i].split("=");k=decodeURIComponent(p[0]);
     v=p[1];v=(v!=null)?decodeURIComponent(v.replace(/\+/g,'%20')):'';
     if (ncnvt && v.trim()>"" && !isNaN(v)) v-=0;
     argarr.push([k,v]);  // array: key-value-pairs of all arguments
   }
   for (i=0,l=argarr.length;i<l;i++) build(argarr[i][0],argarr[i][1],ret);
   return ret;
}
If the function is called without the str-argument it will assume  window.location.search.slice(1) as input.
Some examples:
['a=1&a=2',                               // 1
 'x[y][0][z][]=1',                        // 2
 'hello=[%22world%22]&world=hello',       // 3
 'a=1&a=2&&b&c=3&d=&=e&',                 // 4
 'fld[2][]=2&fld[][]=3&fld[3][]=4&fld[]=bb&fld[]=cc',  // 5
 $.param({a:[[1,2],[3,4],{aa:'one',bb:'two'},[5,6]]}), // 6
 'a[]=hi&a[]=2&a[3][]=7&a[3][]=99&a[]=13',// 7
 'a[x]=hi&a[]=2&a[3][]=7&a[3][]=99&a[]=13'// 8
].map(function(v){return JSON.stringify(getargs(v));}).join('\n')
results in
{"a":2}                                    // 1
{"x":{"y":[{"z":[1]}]}}                    // 2
{"hello":"[\"world\"]","world":"hello"}    // 3
{"a":2,"b":null,"c":3,"d":null,"null":"e"} // 4 = { a: 2, b: null, c: 3, d: null, null: "e" }
{"fld":[null,null,[2],[3,4],"bb","cc"]}    // 5 
{"a":[[1,2],[3,4],{"aa":"one","bb":"two"},[5,6]]}  // 6
{"a":["hi",2,null,[7,99],13]}              // 7
{"a":{"0":2,"3":[7,99],"4":13,"x":"hi"}}   // 8
Whereas Jacky Li's solution would produce the outer container for a as a plain object
{a:{"0":["1","2"],"1":["3","4"],"2":["5","6"]}} // 6: JackyLi's output
getargs() looks at the first given index for any level to determine whether this level will be an object (non-numeric index) or an array (numeric or empty), thus resulting in the output as shown in the listing bove (no. 6).
If the current object is an array then nulls get inserted wherever necessary to represent empty positions. Arrays are always consecutively numbered and 0-based).
Note, that in the example no. 8 the "autoincrement" for empty indices still works, even though we are dealing with an object now and not an array.
As far as I have tested it, my getargs() behaves pretty much identically to Chriss Roger's great jQuery $.deparam() plugin mentioned in the accepted answer. The main difference is that getargs runs without jQuery and that it does autoincrement in objects while $.deparam() will not do that:
JSON.stringify($.deparam('a[x]=hi&a[]=2&a[3][]=7&a[3][]=99&a[]=13').a);
results in
{"3":["7","99"],"x":"hi","undefined":"13"}
In $.deparam() the index [] is interpreted as an undefined instead of an autoincremented numerical index.