Skip to content

Instantly share code, notes, and snippets.

@lsauer
Created October 5, 2011 16:09
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save lsauer/1264847 to your computer and use it in GitHub Desktop.
Save lsauer/1264847 to your computer and use it in GitHub Desktop.
JSON.stringify: Forcing Array notation for JavaScript's serialized function arguments
//author: lo sauer 2011, lsauer.com
//description: Forcing JSON array notation on array-like object (i.e. arguments, DOMCollection)
//application: e.g. JSON-RPC
//Take for instance this simple function, which returns a pure JS object notation
function x(){console.log(JSON.stringify(arguments))}
x(1,2,3,"sfdsf", [1,2,3], {1:2})
>>>[{"0":1,"1":2,"2":3,"3":"sfdsf","4":[1,2,3],"5":{"1":2}}]
//There is no straightforward Array typecasting in JS; Array() will nest the object in an array
function x(){console.log(JSON.stringify.call(null,Array(arguments)))}
x(1,2,3,"sfdsf", [1,2,3], {1:2})
>>>[{"0":1,"1":2,"2":3,"3":"sfdsf","4":[1,2,3],"5":{"1":2}}]
//The reason for the object-notation is that 'arguments' is actually an object and EMACS JSON.stringify correctly serializes the object
//Solutions #1: conversion into an array; works with older JS versions (>1.6)
>>>function x(){for(var args=[], i=0; i<arguments.length; args[i]=arguments[i++]); console.log(JSON.stringify(args))}
//Solutions #2: via apply; no scope is set in this case (i.e. null) JS >1.5
//the latter solution is more powerful by virtue of scope-binding utilization of native functions
//note use Array.apply(null,arguments).slice(startpos[,endpos]) for a subsection of the arguments
function x(){console.log(JSON.stringify( Array.apply(null,arguments) ))}
>>>[1,2,3,"sfdsf",[1,2,3],{"1":2}]
//Note that you can pass as a second parameter to JSON.stringify: a 'stringifier' callback function
//This callback function is passed the key and value of the current element...
//unlike forEach-like functions (e.g. map,filter..) to which also the entire object is passed.
// By selectively returning the value of the passed element, filtering is possible or type-casting
//for objects or values which do not have a representation in JSON such JS's primtive type 'undefined'
function x(){
var stringifier = function (key, val) {
console.log(key,val)
return (val.constructor === String) ? val : void(val);
};
console.log(JSON.stringify( Array.apply(null,arguments), stringifier ))
}
//NOTE:
//apply seems to performs an dimensionality reduction in case of 1 argument
//as such, this case must be treated separately:
Math.log.apply(this, 100)
//as expected; apply takes an argument array as the second parameter
TypeError: Function.prototype.apply: Arguments list has wrong type
Math.log.apply(this, [[100]])
4.605170185988092
Math.log.apply(this, [[[100]]])
4.605170185988092
Math.log.apply(this, [[[[100]]]])
4.605170185988092
Math.log.apply(this, [[[[100,10]]]])
4.605170185988092
//this poses a problem:
x(1)
>>>[]
undefined
x(1,2)
>>>[1, 2]
//The reasons for this is an 'inconsistency' in JS:
Array(1234)
[]
//To overcome this use:
arguments.length===1?arguments : Array.apply(null,arguments)
//e.g.
Math.log.apply(this, [[100]])
function x(){console.log(arguments.length===1?arguments : Array.apply(null,arguments) )}
x(1)
>>>[1]
function min(){return Math.min.apply(null,arguments.length===1?arguments : Array.apply(null,arguments) );}
min(1)
>>>1
min(-1,2,3,4)
>>>-1
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment