Skip to content

Instantly share code, notes, and snippets.

@rjrodger
Created August 7, 2012 12:45
Show Gist options
  • Save rjrodger/3285005 to your computer and use it in GitHub Desktop.
Save rjrodger/3285005 to your computer and use it in GitHub Desktop.
Workaround for property order inconsistency in Chrome V8.
/*
Workaround for property order inconsistency in Chrome V8.
Although unspecified by ECMA, most JavaScript engines return
properties in insertion order when you use for..in to iterate through
them.
For example:
var obj = {red:1,green:1,blue:1}
var order = []
for( var p in obj ) order.push(p);
// p -> ['red','green','blue']
However, this behavior is not consistent. In particular, Chrome, and
hence Node.js, place integer properties first:
{a:1,b:1,"1":1} -> 1, a, b
This is due to the internal V8 implementation. To guarantee order, you
need to use an array.
But this is nasty, especially when using declarative syntax to define
things like configuration. Compare:
{happy:1,days:1}
to
[{name:'happy',value:1},{name:'days',value:1}]
This gets even uglier when you have to use formal JSON.
So using implicit property order is a forgivable sin. It increases developer usability.
But we still have to worry about that gotcha with integer properties -
what happens when someday an integer property is needed? Recoding to
handle an array is a pain, and it's even more of a pain to get your
users to reformat their definition code.
So here's a workaround (it's just a hack): accept __ as a
self-deleting prefix for property names:
{__a:1,__b:1,__1:1} -> a, b, 1 and {a:1,b:1,"1":1}
You just need a function to do, and here it is:
*/
// Return an ordered array of property names. The prefix __ is removed
// from property names, both in the returned array, and the original
// object.
function proporder(obj) {
var pn = []
for( var p in obj ) {
var pc = p
if( 0 == p.indexOf('__') ) {
pc = p.substring(2)
obj[pc] = obj[p]
delete obj[p]
}
pn.push(pc)
}
return pn
}
var assert = require('assert')
assert.equal('c,a,b', ''+proporder({c:1,a:1,b:1}))
assert.equal('1,c,a,b', ''+proporder({c:1,a:1,b:1,"1":1}))
var o = {__c:1,__a:1,__b:1,__1:1}
assert.equal('c,a,b,1', ''+proporder(o))
assert.equal('{"1":1,"c":1,"a":1,"b":1}', JSON.stringify(o))
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment