From faf658465281e311384c845b3a244d6ff1d0591b Mon Sep 17 00:00:00 2001 From: John-David Dalton Date: Fri, 1 Aug 2014 16:36:44 -0700 Subject: [PATCH] Ensure `_.mixin` does not extend lodash when an `object` is provided with an empty `options` object. [closes #650] --- lodash.js | 29 ++++++++++++++++------------- test/test.js | 6 ++++++ 2 files changed, 22 insertions(+), 13 deletions(-) diff --git a/lodash.js b/lodash.js index 8e9a426e5..0f17f7a5d 100644 --- a/lodash.js +++ b/lodash.js @@ -1917,17 +1917,16 @@ } /** - * The base implementation of `_.functions` which creates an array of function - * property names from those returned by `keysFunc`. + * The base implementation of `_.functions` which creates an array of + * `object` function property names filtered from those provided. * * @private * @param {Object} object The object to inspect. - * @param {Function} keysFunc The function to get the keys of `object`. - * @returns {Array} Returns the new sorted array of property names. + * @param {Array} props The property names to filter. + * @returns {Array} Returns the new array of filtered property names. */ - function baseFunctions(object, keysFunc) { + function baseFunctions(object, props) { var index = -1, - props = keysFunc(object), length = props.length, resIndex = -1, result = []; @@ -6848,14 +6847,14 @@ * @alias methods * @category Object * @param {Object} object The object to inspect. - * @returns {Array} Returns the new sorted array of property names. + * @returns {Array} Returns the new array of property names. * @example * * _.functions(_); * // => ['all', 'any', 'bind', 'bindAll', 'clone', 'compact', 'compose', ...] */ function functions(object) { - return baseFunctions(object, keysIn); + return baseFunctions(object, keysIn(object)); } /** @@ -8784,16 +8783,20 @@ */ function mixin(object, source, options) { var chain = true, - methodNames = source && baseFunctions(source, keys); + isObj = isObject(source), + noOpts = options == null, + props = noOpts && isObj && keys(source), + methodNames = props && baseFunctions(source, props); - if (!source || (!options && !methodNames.length)) { - if (options == null) { + if (!source || (props && props.length && !methodNames.length) || (noOpts && !isObj)) { + if (noOpts) { options = source; } + methodNames = false; source = object; object = this; - methodNames = baseFunctions(source, keys); } + methodNames || (methodNames = baseFunctions(source, keys(source))); if (options === false) { chain = false; } else if (isObject(options) && 'chain' in options) { @@ -8801,7 +8804,7 @@ } var index = -1, isFunc = isFunction(object), - length = methodNames ? methodNames.length : 0; + length = methodNames.length; while (++index < length) { var methodName = methodNames[index], diff --git a/test/test.js b/test/test.js index 918bb88ec..61e9e4130 100644 --- a/test/test.js +++ b/test/test.js @@ -7554,6 +7554,12 @@ }); }); + test('should not extend lodash when an `object` is provided with an empty `options` object', 1, function() { + _.mixin({ 'a': _.noop }, {}); + ok(!('a' in _)); + delete _.a; + }); + test('should not error for non-object `options` values', 2, function() { var pass = true;