Created
August 7, 2012 12:45
-
-
Save rjrodger/3285005 to your computer and use it in GitHub Desktop.
Workaround for property order inconsistency in Chrome V8.
This file contains hidden or 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
/* | |
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