Fix "prototype" iteration bug with _.keys.

Former-commit-id: 1e072b2639e21a5c0a920db0ac27693ade34b009
This commit is contained in:
John-David Dalton
2012-05-31 10:29:33 -05:00
parent b432721fe5
commit 861eea5148
3 changed files with 43 additions and 19 deletions

View File

@@ -439,25 +439,19 @@
if (isMobile) { if (isMobile) {
// inline functions defined with `createIterator` // inline functions defined with `createIterator`
lodash.functions(lodash).forEach(function(funcName) { lodash.functions(lodash).forEach(function(funcName) {
var reFunc = RegExp('( +var ' + funcName + ' *= *)((?:[a-zA-Z]+ *\\|\\| *)?)createIterator\\(((?:{|[a-zA-Z])[\\s\\S]+?)\\);\\n'), var reFunc = RegExp('(\\bvar ' + funcName.replace(/^_/, '') + ' *= *)createIterator\\(((?:{|[a-zA-Z])[\\s\\S]+?)\\);\\n');
parts = source.match(reFunc);
// skip if not defined with `createIterator` // skip if not defined with `createIterator`
if (!parts) { if (!reFunc.test(source)) {
return; return;
} }
// extract function's code // extract and format the function's code
var code = funcName == 'keys' var code = (lodash[funcName] + '').replace(/\n(?:.*)/g, function(match) {
? '$1$2' + lodash._createIterator(Function('return ' + parts[3])())
: '$1' + lodash[funcName];
// format code
code = code.replace(/\n(?:.*)/g, function(match) {
match = match.slice(1); match = match.slice(1);
return (match == '}' ? '\n ' : '\n ') + match; return (match == '}' ? '\n ' : '\n ') + match;
}) + ';\n'; });
source = source.replace(reFunc, code); source = source.replace(reFunc, '$1' + code + ';\n');
}); });
// remove `iteratorTemplate` // remove `iteratorTemplate`
@@ -490,7 +484,7 @@
} }
// remove pseudo private properties // remove pseudo private properties
source = source.replace(/(?:\s*\/\/.*)*\s*lodash\._(?:createIterator|iteratorTemplate)\b.+\n/g, '\n'); source = source.replace(/(?:\s*\/\/.*)*\s*lodash\._[^=]+=.+\n/g, '\n');
/*--------------------------------------------------------------------------*/ /*--------------------------------------------------------------------------*/

View File

@@ -477,6 +477,21 @@
// no operation performed // no operation performed
} }
/**
* A shim implementation of `Object.keys` that produces an array of the given
* object's enumerable own property names.
*
* @private
* @param {Object} object The object to inspect.
* @returns {Array} Returns a new array of property names.
*/
var shimKeys = createIterator({
'args': 'object',
'exit': 'if (!objectTypes[typeof object] || object === null) throw TypeError()',
'init': '[]',
'inLoop': 'result.push(index)'
});
/** /**
* Used by `template()` to replace "escape" template delimiters with tokens. * Used by `template()` to replace "escape" template delimiters with tokens.
* *
@@ -2643,12 +2658,12 @@
* _.keys({ 'one': 1, 'two': 2, 'three': 3 }); * _.keys({ 'one': 1, 'two': 2, 'three': 3 });
* // => ['one', 'two', 'three'] * // => ['one', 'two', 'three']
*/ */
var keys = nativeKeys || createIterator({ var keys = !nativeKeys ? shimKeys : function(object) {
'args': 'object', // avoid iterating over the `prototype` property
'exit': 'if (!objectTypes[typeof object] || object === null) throw TypeError()', return typeof object == 'function'
'init': '[]', ? shimKeys(object)
'inLoop': 'result.push(index)' : nativeKeys(object);
}); };
/** /**
* Creates an object composed of the specified properties. Property names may * Creates an object composed of the specified properties. Property names may
@@ -3209,6 +3224,7 @@
// add pseudo privates used and removed during the build process // add pseudo privates used and removed during the build process
lodash._createIterator = createIterator; lodash._createIterator = createIterator;
lodash._iteratorTemplate = iteratorTemplate; lodash._iteratorTemplate = iteratorTemplate;
lodash._shimKeys = shimKeys;
/*--------------------------------------------------------------------------*/ /*--------------------------------------------------------------------------*/

View File

@@ -306,6 +306,20 @@
deepEqual(_.keys(shadowed).sort(), deepEqual(_.keys(shadowed).sort(),
'constructor hasOwnProperty isPrototypeOf propertyIsEnumerable toLocaleString toString valueOf'.split(' ')); 'constructor hasOwnProperty isPrototypeOf propertyIsEnumerable toLocaleString toString valueOf'.split(' '));
}); });
test('skips the prototype property of functions (test in Firefox < 3.6, Opera > 9.50 - Opera < 11.60, and Safari < 5.1)', function() {
function Foo() {}
Foo.prototype.c = 3;
Foo.a = 1;
Foo.b = 2;
var expected = ['a', 'b'];
deepEqual(_.keys(Foo), expected);
Foo.prototype = { 'c': 3 };
deepEqual(_.keys(Foo), expected);
});
}()); }());
/*--------------------------------------------------------------------------*/ /*--------------------------------------------------------------------------*/