Created
October 5, 2011 16:09
-
-
Save lsauer/1264847 to your computer and use it in GitHub Desktop.
JSON.stringify: Forcing Array notation for JavaScript's serialized function arguments
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
//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