将对象的所有键都变成小写的最好方法是什么(最有效)?

我已经想出来了

function keysToLowerCase (obj) { var keys = Object.keys(obj); var n = keys.length; while (n--) { var key = keys[n]; // "cache" it, for less lookups to the array if (key !== key.toLowerCase()) { // might already be in its lower case version obj[key.toLowerCase()] = obj[key] // swap the value to a new lower case key delete obj[key] // delete the old key } } return (obj); } 

但我不确定v8会如何performance,例如,它真的会删除其他键,还是只会删除引用,垃圾收集器会在晚些时候咬我?

此外,我创build了这些testing ,我希望你可以在那里添加你的答案,所以我们可以看到他们如何匹配。

编辑1:显然,根据testing,如果我们不检查键是否已经是小写字母,但是速度更快一些,会更快,忽略这一点,只是创build新的小写键? 垃圾收集器会对此感到满意吗?

我想出的最快的是如果你创build一个新的对象:

 var key, keys = Object.keys(obj); var n = keys.length; var newobj={} while (n--) { key = keys[n]; newobj[key.toLowerCase()] = obj[key]; } 

我对目前v8的内部工作不够熟悉,给你一个明确的答案。 几年前,我看到一个开发者谈论对象的video,IIRC只会删除引用,并让垃圾收集器处理它。 但是几年前,即使是那样的话,现在也不需要那样。

它会在晚些时候咬你吗? 这取决于你在做什么,但可能不是。 创build短暂的对象是非常常见的,所以代码被优化来处理它。 但是每个环境都有其局限性,也许会咬你。 你必须用实际数据进行testing。

2017年更新:

添加了一个实用function,可以使对象的浅层或深层复制,支持循环引用。 只在节点上进行简短的testing

 /** @summary objectKeysToLowerCase( input, deep, filter ) * returns a new object with all own keys converted to lower case. * The copy can be shallow (default) or deep. * * Circular references is supported during deep copy and the output will have * the same structure. * * By default only objects that have Object as constructor is copied. * It can be changed with the "filter"-function. * * NOTE: If an object has multiple keys that only differs in case, * only the value of the last seen key is saved. The order is usually * in the order that the keys where created. * Exaple : input = {aa:1, aA:2, Aa:3, AA:4}, output = {aa:4}; * * NOTE: To detect circular references, the list of objects already converted * is searched for every new object. If you have too many objects, it will * be slower and slower... * * @param {object} input * The source object * @param {boolean|number} deep * A shallow copy is made if "deep" is undefined, null, false or 0. * A deep copy is made if "deep" is true or a positive number. * The number specifies how many levels to copy. Infinity is a valid number. * This variable is used internally during deep copy. * @param {function} filter * A filter function(object) to filter objects that should be copied. * If it returns true, the copy is performed. * @returns {object} * */ function objectKeysToLowerCase( input, deep, filter ) { var idx, key, keys, last, output, self, type, value; self = objectKeysToLowerCase; type = typeof deep; // Convert "deep" to a number between 0 to Infinity or keep special object. if ( type === 'undefined' || deep === null || deep === 0 || deep === false ) { deep = 0; // Shallow copy } else if ( type === 'object' ) { if ( !( deep instanceof self ) ) { throw new TypeError( 'Expected "deep" to be a special object' ); } } else if ( deep === true ) { deep = Infinity; // Deep copy } else if ( type === 'number' ) { if ( isNaN(deep) || deep < 0 ) { throw new RangeError( 'Expected "deep" to be a positive number, got ' + deep ); } } else throw new TypeError( 'Expected "deep" to be a boolean, number or object, got "' + type + '"' ); // Check type of input, and throw if null or not an object. if ( input === null || typeof input !== 'object' ) { throw new TypeError( 'Expected "input" to be an object' ); } // Check type of filter type = typeof filter; if ( filter === null || type === 'undefined' || type === 'function' ) { filter = filter || null; } else { throw new TypeError( 'Expected "filter" to be a function' ); } keys = Object.keys(input); // Get own keys from object last = keys.length - 1; output = {}; // new object if (deep) { // only run the deep copy if needed. if (typeof deep === 'number') { // Create special object to be used during deep copy deep = Object.seal( Object.create( self.prototype, { input : { value : [] }, output : { value : [] }, level : { value : -1, writable:true }, max : { value : deep, writable:false } } ) ); } else { // Circle detection idx = deep.input.indexOf( input ); if ( ~idx ) { return deep.output[ idx ]; } } deep.level += 1; deep.input.push( input ); deep.output.push( output ); idx = last + 1; while ( idx-- ) { key = keys[ last - idx ]; // Using [last - idx] to preserve order. value = input[ key ]; if ( typeof value === 'object' && value && deep.level < deep.max ) { if ( filter ? filter(value) : value.constructor === Object ) { value = self( value, deep, filter ); } } output[ key.toLowerCase() ] = value; } deep.level -= 1; } else { // Simple shallow copy idx = last + 1; while ( idx-- ) { key = keys[ last - idx ]; // Using [last - idx] to preserve order. output[ key.toLowerCase() ] = input[ key ]; } } return output; } 

我会用这样的Lo-Dash.transform :

 var lowerObj = _.transform(obj, function (result, val, key) { result[key.toLowerCase()] = val; }); 

就个人而言,我会使用:

 let objectKeysToLowerCase = function (origObj) { return Object.keys(origObj).reduce(function (newObj, key) { let val = origObj[key]; let newVal = (typeof val === 'object') ? objectKeysToLowerCase(val) : val; newObj[key.toLowerCase()] = newVal; return newObj; }, {}); } 

它简洁,重复处理嵌套的对象,并返回一个新的对象,而不是修改原来的。

在我有限的本地testing中,此function比目前列出的其他recursion解决scheme(一旦修复)要快。 我很想把它和其他人做比较,但是jsperf现在已经下降了(???)。

它也是用ES5.1编写的,所以根据MDN上的文档,应该可以在FF 4+,Chrome 5+,IE 9.0+,Opera 12+,Safari 5+(几乎所有的东西)中工作。

  • Object.keys()
  • arrays。 prototype.reduce()

香草JS为胜利。

我不会太担心所有这些垃圾收集方面的问题。 一旦旧对象的所有引用都被销毁,它将是GC的,但对象仍然会引用基本上所有的属性,所以它们不会。

任何函数,数组或RegExp将通过引用“复制”。 就内存而言,甚至string也不会被这个过程复制,因为大多数(所有)现代JS引擎用户string实际上都是这样 。 我认为只留下形成原始结构的数字,布尔和对象就留给了GC。

请注意,如果原始文件具有多个具有相同小写字母表示的属性,则此过程(的所有实现)将丢失值。 即:

 let myObj = { xx: 'There', xX: 'can be', Xx: 'only', XX: 'one!' }; console.log(myObj); // { xx: 'There', xX: 'can be', Xx: 'only', XX: 'one!' } let newObj = objectKeysToLowerCase(myObj); console.log(newObj); // { xx: 'one!' } 

当然,有时这正是你想要的。

在我的testing中使用forEach似乎更快一些 – 原始参考已经消失了,所以删除新的将会把它放到gc的范围内

 function keysToLowerCase(obj){ Object.keys(obj).forEach(function (key) { var k = key.toLowerCase(); if (k !== key) { obj[k] = obj[key]; delete obj[key]; } }); return (obj); } 

var O = {ONE:1,2:2,tHree:3,FOUR:4,Five:5,SIX:{a:1,b:2,c:3,D:4,E:5}}; keysToLowerCase(O);

/ *返回值:(Object)* /

 { five:5, four:4, one:1, six:{ a:1, b:2, c:3, D:4, E:5 }, three:3, two:2 } 

考虑只降低一次,将其存储在一个lowKey var:

 function keysToLowerCase (obj) { var keys = Object.keys(obj); var n = keys.length; var lowKey; while (n--) { var key = keys[n]; if (key === (lowKey = key.toLowerCase())) continue obj[lowKey] = obj[key] delete obj[key] } return (obj); } 

这是基于上述示例之一的recursion版本。

 //updated function var lowerObjKeys = function(obj) { Object.keys(obj).forEach(function(key) { var k = key.toLowerCase(); if (k != key) { var v = obj[key] obj[k] = v; delete obj[key]; if (typeof v == 'object') { lowerObjKeys(v); } } }); return obj; } //plumbing console = { _createConsole: function() { var pre = document.createElement('pre'); pre.setAttribute('id', 'console'); document.body.insertBefore(pre, document.body.firstChild); return pre; }, info: function(message) { var pre = document.getElementById("console") || console._createConsole(); pre.textContent += ['>', message, '\n'].join(' '); } }; //test case console.info(JSON.stringify(lowerObjKeys({ "StackOverflow": "blah", "Test": { "LULZ": "MEH" } }), true)); 

loDash / fp的方式,相当不错,因为它基本上是一个class轮

 import { mapKeys } from 'lodash/fp' export function lowerCaseObjectKeys (value) { return mapKeys(k => k.toLowerCase(), value) }