diff --git a/dist/lodash.compat.js b/dist/lodash.compat.js index c2b9446e2..9eb31c288 100644 --- a/dist/lodash.compat.js +++ b/dist/lodash.compat.js @@ -12,14 +12,7 @@ /** Used as a safe reference for `undefined` in pre ES5 environments */ var undefined; - /** Used to pool arrays and objects used internally */ - var arrayPool = [], - objectPool = []; - - /** Used to generate unique IDs */ - var idCounter = 0; - - /** Used to compose bitmasks for `__bindData__` */ + /** Used to compose bitmasks for wrapper metadata */ var BIND_FLAG = 1, BIND_KEY_FLAG = 2, CURRY_FLAG = 4, @@ -27,23 +20,20 @@ PARTIAL_FLAG = 16, PARTIAL_RIGHT_FLAG = 32; - /** Used as the size when optimizations are enabled for large arrays */ + /** Used as the size when optimizations are enabled for arrays */ var LARGE_ARRAY_SIZE = 75; /** Used as the max size of the `arrayPool` and `objectPool` */ var MAX_POOL_SIZE = 40; - /** Used to detect and test whitespace */ - var whitespace = ( - // whitespace - ' \t\x0B\f\xA0\ufeff' + + /** Used as the semantic version number */ + var version = '2.4.1'; - // line terminators - '\n\r\u2028\u2029' + + /** Used as the property name for wrapper metadata */ + var expando = '__lodash@' + version + '__'; - // unicode category "Zs" space separators - '\u1680\u180E\u2000\u2001\u2002\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200a\u202f\u205f\u3000' - ); + /** Used to generate unique IDs */ + var idCounter = 0; /** Used to match empty string literals in compiled template source */ var reEmptyStringLeading = /\b__p \+= '';/g, @@ -83,6 +73,22 @@ /** Used to match unescaped characters in compiled string literals */ var reUnescapedString = /['\n\r\t\u2028\u2029\\]/g; + /** Used to detect and test whitespace */ + var whitespace = ( + // whitespace + ' \t\x0B\f\xA0\ufeff' + + + // line terminators + '\n\r\u2028\u2029' + + + // unicode category "Zs" space separators + '\u1680\u180E\u2000\u2001\u2002\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200a\u202f\u205f\u3000' + ); + + /** Used to pool arrays and objects used internally */ + var arrayPool = [], + objectPool = []; + /** Used to assign default `context` object properties */ var contextProps = [ 'Array', 'Boolean', 'Date', 'Error', 'Function', 'Math', 'Number', 'Object', @@ -126,7 +132,7 @@ 'trailing': false }; - /** Used as the property descriptor for `__bindData__` */ + /** Used as the property descriptor for wrapper metadata */ var descriptor = { 'configurable': false, 'enumerable': false, @@ -1043,13 +1049,13 @@ * sets its meta data. * * @private - * @param {Array} bindData The bind data array. + * @param {Array} data The metadata array. * @returns {Function} Returns the new bound function. */ - function baseBind(bindData) { - var func = bindData[0], - partialArgs = bindData[2], - thisArg = bindData[4]; + function baseBind(data) { + var func = data[0], + thisArg = data[3], + partialArgs = data[4]; function bound() { // `Function#bind` spec @@ -1071,7 +1077,7 @@ } return func.apply(thisArg, args || arguments); } - setBindData(bound, bindData); + setData(bound, data); return bound; } @@ -1211,26 +1217,26 @@ if (typeof thisArg == 'undefined' || !('prototype' in func)) { return func; } - var bindData = func.__bindData__; - if (typeof bindData == 'undefined') { + var data = func[expando]; + if (typeof data == 'undefined') { if (support.funcNames) { - bindData = !func.name; + data = !func.name; } - bindData = bindData || !support.funcDecomp; - if (!bindData) { + data = data || !support.funcDecomp; + if (!data) { var source = fnToString.call(func); if (!support.funcNames) { - bindData = !reFuncName.test(source); + data = !reFuncName.test(source); } - if (!bindData) { + if (!data) { // checks if `func` references the `this` keyword and stores the result - bindData = reThis.test(source); - setBindData(func, bindData); + data = reThis.test(source); + setData(func, data); } } } // exit early if there are no `this` references or `func` is bound - if (bindData === false || (bindData !== true && bindData[1] & BIND_FLAG)) { + if (data === false || (data !== true && data[1] & BIND_FLAG)) { return func; } switch (argCount) { @@ -1255,16 +1261,16 @@ * sets its meta data. * * @private - * @param {Array} bindData The bind data array. + * @param {Array} data The metadata array. * @returns {Function} Returns the new function. */ - function baseCreateWrapper(bindData) { - var func = bindData[0], - bitmask = bindData[1], - partialArgs = bindData[2], - partialRightArgs = bindData[3], - thisArg = bindData[4], - arity = bindData[5]; + function baseCreateWrapper(data) { + var func = data[0], + bitmask = data[1], + arity = data[2], + thisArg = data[3], + partialArgs = data[4], + partialRightArgs = data[5]; var isBind = bitmask & BIND_FLAG, isBindKey = bitmask & BIND_KEY_FLAG, @@ -1283,10 +1289,15 @@ if (partialRightArgs) { push.apply(args, partialRightArgs); } - if (isCurry && args.length < arity) { + var argsLength = arguments.length; + if (isCurry && argsLength < arity) { bitmask |= PARTIAL_FLAG; - bitmask &= ~PARTIAL_RIGHT_FLAG; - return baseCreateWrapper([func, (isCurryBound ? bitmask : bitmask & ~(BIND_FLAG | BIND_KEY_FLAG)), args, null, thisArg, arity]); + bitmask &= ~PARTIAL_RIGHT_FLAG + if (!isCurryBound) { + bitmask &= ~(BIND_FLAG | BIND_KEY_FLAG); + } + var newArity = nativeMax(0, arity - argsLength); + return baseCreateWrapper([func, bitmask, newArity, thisArg, args]); } } args || (args = arguments); @@ -1300,7 +1311,7 @@ } return func.apply(thisBinding, args); } - setBindData(bound, bindData); + setData(bound, data); return bound; } @@ -1779,25 +1790,23 @@ * @param {Function|string} func The function or method name to reference. * @param {number} bitmask The bitmask of flags to compose. * The bitmask may be composed of the following flags: - * 1 - `_.bind` - * 2 - `_.bindKey` - * 4 - `_.curry` - * 8 - `_.curry` (bound) + * 1 - `_.bind` + * 2 - `_.bindKey` + * 4 - `_.curry` + * 8 - `_.curry` (bound) * 16 - `_.partial` * 32 - `_.partialRight` + * @param {number} [arity] The arity of `func`. + * @param {*} [thisArg] The `this` binding of `func`. * @param {Array} [partialArgs] An array of arguments to prepend to those * provided to the new function. * @param {Array} [partialRightArgs] An array of arguments to append to those * provided to the new function. - * @param {*} [thisArg] The `this` binding of `func`. - * @param {number} [arity] The arity of `func`. * @returns {Function} Returns the new function. */ - function createWrapper(func, bitmask, partialArgs, partialRightArgs, thisArg, arity) { + function createWrapper(func, bitmask, arity, thisArg, partialArgs, partialRightArgs) { var isBind = bitmask & BIND_FLAG, isBindKey = bitmask & BIND_KEY_FLAG, - isCurry = bitmask & CURRY_FLAG, - isCurryBound = bitmask & CURRY_BOUND_FLAG, isPartial = bitmask & PARTIAL_FLAG, isPartialRight = bitmask & PARTIAL_RIGHT_FLAG; @@ -1805,50 +1814,69 @@ throw new TypeError; } if (isPartial && !partialArgs.length) { - bitmask &= ~16; + bitmask &= ~PARTIAL_FLAG; isPartial = partialArgs = false; } if (isPartialRight && !partialRightArgs.length) { - bitmask &= ~32; + bitmask &= ~PARTIAL_RIGHT_FLAG; isPartialRight = partialRightArgs = false; } - var bindData = func && func.__bindData__; - if (bindData && bindData !== true) { - // clone `bindData` - bindData = slice(bindData); - if (bindData[2]) { - bindData[2] = slice(bindData[2]); + var data = !isBindKey && func[expando]; + if (data && data !== true) { + // shallow clone `data` + data = slice(data); + + // clone partial left arguments + if (data[4]) { + data[4] = slice(data[4]); } - if (bindData[3]) { - bindData[3] = slice(bindData[3]); + // clone partial right arguments + if (data[5]) { + data[5] = slice(data[5]); } - // set `thisBinding` is not previously bound - if (isBind && !(bindData[1] & BIND_FLAG)) { - bindData[4] = thisArg; + // set arity if provided + if (typeof arity == 'number') { + data[2] = arity; } - // set if previously bound but not currently (subsequent curried functions) - if (!isBind && bindData[1] & BIND_FLAG) { - bitmask |= 8; + // set `thisArg` if not previously bound + var bound = data[1] & BIND_FLAG; + if (isBind && !bound) { + data[3] = thisArg; } - // set curried arity if not yet set - if (isCurry && !(bindData[1] & CURRY_FLAG)) { - bindData[5] = arity; + // set if currying a bound function + if (!isBind && bound) { + bitmask |= CURRY_BOUND_FLAG; } // append partial left arguments if (isPartial) { - push.apply(bindData[2] || (bindData[2] = []), partialArgs); + if (data[4]) { + push.apply(data[4], partialArgs); + } else { + data[4] = partialArgs; + } } - // append partial right arguments + // prepend partial right arguments if (isPartialRight) { - unshift.apply(bindData[3] || (bindData[3] = []), partialRightArgs); + if (data[5]) { + unshift.apply(data[5], partialRightArgs); + } else { + data[5] = partialRightArgs; + } } // merge flags - bindData[1] |= bitmask; - return createWrapper.apply(null, bindData); + data[1] |= bitmask; + return createWrapper.apply(null, data); + } + if (arity == null) { + arity = isBindKey ? 0 : func.length; + } else if (arity < 0) { + arity = 0; } // fast path for `_.bind` - var creater = (bitmask == BIND_FLAG || bitmask == (BIND_FLAG | PARTIAL_FLAG)) ? baseBind : baseCreateWrapper; - return creater([func, bitmask, partialArgs, partialRightArgs, thisArg, arity]); + data = [func, bitmask, arity, thisArg, partialArgs, partialRightArgs]; + return (bitmask == BIND_FLAG || bitmask == (BIND_FLAG | PARTIAL_FLAG)) + ? baseBind(data) + : baseCreateWrapper(data); } /** @@ -1904,15 +1932,15 @@ } /** - * Sets `this` binding data on a given function. + * Sets wrapper metadata on a given function. * * @private * @param {Function} func The function to set data on. * @param {Array} value The data array to set. */ - var setBindData = !defineProperty ? noop : function(func, value) { + var setData = !defineProperty ? noop : function(func, value) { descriptor.value = value; - defineProperty(func, '__bindData__', descriptor); + defineProperty(func, expando, descriptor); }; /** @@ -4500,9 +4528,14 @@ * // => 'hi fred' */ function bind(func, thisArg) { - return arguments.length > 2 - ? createWrapper(func, BIND_FLAG | PARTIAL_FLAG, slice(arguments, 2), null, thisArg) - : createWrapper(func, BIND_FLAG, null, null, thisArg); + if (arguments.length < 3) { + return createWrapper(func, BIND_FLAG, null, thisArg); + } + var arity = func && (func[expando] ? func[expando][2] : func.length), + partialArgs = slice(arguments, 2); + + arity -= partialArgs.length; + return createWrapper(func, BIND_FLAG | PARTIAL_FLAG, arity, thisArg, partialArgs); } /** @@ -4538,7 +4571,7 @@ while (++index < length) { var key = funcs[index]; - object[key] = createWrapper(object[key], BIND_FLAG, null, null, object); + object[key] = createWrapper(object[key], BIND_FLAG, null, object); } return object; } @@ -4579,9 +4612,9 @@ * // => 'hiya fred!' */ function bindKey(object, key) { - return arguments.length > 2 - ? createWrapper(key, BIND_FLAG | BIND_KEY_FLAG | PARTIAL_FLAG, slice(arguments, 2), null, object) - : createWrapper(key, BIND_FLAG | BIND_KEY_FLAG, null, null, object); + return arguments.length < 3 + ? createWrapper(key, BIND_FLAG | BIND_KEY_FLAG, null, object) + : createWrapper(key, BIND_FLAG | BIND_KEY_FLAG | PARTIAL_FLAG, null, object, slice(arguments, 2)); } /** @@ -4665,8 +4698,10 @@ * // => 6 */ function curry(func, arity) { - arity = typeof arity == 'number' ? arity : (+arity || func.length); - return createWrapper(func, CURRY_FLAG, null, null, null, arity); + if (typeof arity != 'number') { + arity = +arity || null; + } + return createWrapper(func, CURRY_FLAG, arity); } /** @@ -4968,7 +5003,11 @@ * // => 'hi fred' */ function partial(func) { - return createWrapper(func, PARTIAL_FLAG, slice(arguments, 1)); + var arity = func && (func[expando] ? func[expando][2] : func.length), + partialArgs = slice(arguments, 1); + + arity -= partialArgs.length; + return createWrapper(func, PARTIAL_FLAG, arity, null, partialArgs); } /** @@ -5002,7 +5041,11 @@ * // => { '_': _, 'jq': $ } */ function partialRight(func) { - return createWrapper(func, PARTIAL_RIGHT_FLAG, null, slice(arguments, 1)); + var arity = func && (func[expando] ? func[expando][2] : func.length), + partialRightArgs = slice(arguments, 1); + + arity -= partialRightArgs.length; + return createWrapper(func, PARTIAL_RIGHT_FLAG, arity, null, null, partialRightArgs); } /** @@ -5078,7 +5121,7 @@ * // => '
fred, barney, & pebbles
' */ function wrap(value, wrapper) { - return createWrapper(wrapper, PARTIAL_FLAG, [value]); + return createWrapper(wrapper, PARTIAL_FLAG, null, null, [value]); } /*--------------------------------------------------------------------------*/ @@ -7449,7 +7492,7 @@ * @memberOf _ * @type string */ - lodash.VERSION = '2.4.1'; + lodash.VERSION = version; // add "Chaining" functions to the wrapper lodash.prototype.chain = wrapperChain; diff --git a/dist/lodash.compat.min.js b/dist/lodash.compat.min.js index 1f835803c..b03718b84 100644 --- a/dist/lodash.compat.min.js +++ b/dist/lodash.compat.min.js @@ -1,63 +1,68 @@ /** * @license - * Lo-Dash 2.4.1 (Custom Build) lodash.com/license | Underscore.js 1.5.2 underscorejs.org/LICENSE + * Lo-Dash 2.4.1 (Custom Build)fred, barney, & pebbles
' */ function wrap(value, wrapper) { - return createWrapper(wrapper, PARTIAL_FLAG, [value]); + return createWrapper(wrapper, PARTIAL_FLAG, null, null, [value]); } /*--------------------------------------------------------------------------*/ @@ -7165,7 +7208,7 @@ * @memberOf _ * @type string */ - lodash.VERSION = '2.4.1'; + lodash.VERSION = version; // add "Chaining" functions to the wrapper lodash.prototype.chain = wrapperChain; diff --git a/dist/lodash.min.js b/dist/lodash.min.js index 857960e65..972e59f21 100644 --- a/dist/lodash.min.js +++ b/dist/lodash.min.js @@ -1,59 +1,63 @@ /** * @license - * Lo-Dash 2.4.1 (Custom Build) lodash.com/license | Underscore.js 1.5.2 underscorejs.org/LICENSE + * Lo-Dash 2.4.1 (Custom Build)fred, barney, & pebbles
' */ function wrap(value, wrapper) { - return createWrapper(wrapper, PARTIAL_FLAG, [value]); + return createWrapper(wrapper, PARTIAL_FLAG, null, null, [value]); } /*--------------------------------------------------------------------------*/ @@ -7466,7 +7509,7 @@ * @memberOf _ * @type string */ - lodash.VERSION = '2.4.1'; + lodash.VERSION = version; // add "Chaining" functions to the wrapper lodash.prototype.chain = wrapperChain; diff --git a/test/test.js b/test/test.js index 17fd3d385..a90f947be 100644 --- a/test/test.js +++ b/test/test.js @@ -1,6 +1,9 @@ ;(function(root, undefined) { 'use strict'; + /** Used as the size when optimizations are enabled for arrays */ + var LARGE_ARRAY_SIZE = 75; + /** Used to store Lo-Dash to test for bad extensions/shims */ var lodashBizarro = root.lodashBizarro; @@ -144,15 +147,15 @@ (_.runInContext ? _.runInContext(root) : _) )); + /** Used as the property name for wrapper metadata */ + var expando = '__lodash@' + _.VERSION + '__'; + /** Used to pass falsey values to methods */ var falsey = [, '', 0, false, NaN, null, undefined]; /** Used to pass empty values to methods */ var empties = [[], {}].concat(falsey.slice(1)); - /** Used as the size when optimizations are enabled for large arrays */ - var LARGE_ARRAY_SIZE = 75; - /** Used to set property descriptors */ var defineProperty = (function() { try { @@ -468,7 +471,7 @@ } catch(e) { actual = null; } - equal('__bindData__' in actual, false, message('Object.defineProperty')); + equal(expando in actual, false, message('Object.defineProperty')); try { actual = [lodashBizarro.isPlainObject({}), lodashBizarro.isPlainObject([])]; @@ -1503,7 +1506,7 @@ } }); - test('should only write `__bindData__` to named functions', 3, function() { + test('should only write metadata to named functions', 3, function() { function a() {}; function c() {}; @@ -1512,16 +1515,16 @@ if (defineProperty && _.support.funcDecomp) { _.createCallback(a, object); - ok('__bindData__' in a); + ok(expando in a); _.createCallback(b, object); - equal('__bindData__' in b, false); + equal(expando in b, false); if (_.support.funcNames) { _.support.funcNames = false; _.createCallback(c, object); - ok('__bindData__' in c); + ok(expando in c); _.support.funcNames = true; } else { @@ -1533,12 +1536,12 @@ } }); - test('should not write `__bindData__` when `_.support.funcDecomp` is `false`', 1, function() { + test('should not write metadata when `_.support.funcDecomp` is `false`', 1, function() { function a() {}; if (defineProperty && lodashBizarro) { lodashBizarro.createCallback(a, {}); - equal('__bindData__' in a, false); + equal(expando in a, false); } else { skipTest(); @@ -1557,13 +1560,12 @@ test('should curry based on the number of arguments provided', 3, function() { var curried = _.curry(func); - equal(curried(1)(2)(3), 6); equal(curried(1, 2)(3), 6); equal(curried(1, 2, 3), 6); }); - test('should work with partial methods', 2, function() { + test('should work with partialed methods', 2, function() { var curried = _.curry(func), a = _.partial(curried, 1), b = _.partialRight(a, 3), @@ -1576,7 +1578,6 @@ test('should return a function with a `length` of `0`', 6, function() { _.times(2, function(index) { var curried = index ? _.curry(func, 4) : _.curry(func); - strictEqual(curried.length, 0); strictEqual(curried(1).length, 0); strictEqual(curried(1, 2).length, 0); @@ -1599,9 +1600,7 @@ var value = this || {}; return value[a] + value[b] + value[c]; } - var object = { 'a': 1, 'b': 2, 'c': 3 }; - equal(_.curry(_.bind(func, object), 3)('a')('b')('c'), 6); equal(_.curry(_.bind(func, object), 3)('a', 'b')('c'), 6); equal(_.curry(_.bind(func, object), 3)('a', 'b', 'c'), 6); @@ -1611,7 +1610,6 @@ equal(_.bind(_.curry(func), object)('a', 'b', 'c'), 6); object.func = _.curry(func); - ok(_.isEqual(object.func('a')('b')('c'), NaN)); ok(_.isEqual(object.func('a', 'b')('c'), NaN)); equal(object.func('a', 'b', 'c'), 6); @@ -5862,22 +5860,18 @@ isPartial = methodName == 'partial'; test('`_.' + methodName + '` partially applies without additional arguments', 1, function() { - var arg = 'a', - fn = function(x) { return x; }; - - equal(func(fn, arg)(), arg); + var fn = function(a) { return a; }; + equal(func(fn, 'a')(), 'a'); }); test('`_.' + methodName + '` partially applies with additional arguments', 1, function() { - var arg1 = 'a', - arg2 = 'b', - expected = [arg1, arg2], - fn = function(x, y) { return [x, y]; }; + var expected = ['a', 'b'], + fn = function(a, b) { return [a, b]; }; if (!isPartial) { expected.reverse(); } - deepEqual(func(fn, arg1)(arg2), expected); + deepEqual(func(fn, 'a')('b'), expected); }); test('`_.' + methodName + '` works without partially applying arguments, without additional arguments', 1, function() { @@ -5886,10 +5880,8 @@ }); test('`_.' + methodName + '` works without partially applying arguments, with additional arguments', 1, function() { - var arg = 'a', - fn = function(x) { return x; }; - - equal(func(fn)(arg), arg); + var fn = function(a) { return a; }; + equal(func(fn)('a'), 'a'); }); test('`_.' + methodName + '` should not alter the `this` binding', 3, function() { @@ -5910,7 +5902,7 @@ strictEqual(actual.length, 0); }); - test('ensure `new bound` is an instance of `func`', 2, function() { + test('`_.' + methodName + '` ensure `new bound` is an instance of `func`', 2, function() { function Foo(value) { return value && object; } @@ -5921,7 +5913,7 @@ strictEqual(new bound(true), object); }); - test('`_.' + methodName + '` should clone `__bindData__` for created functions', 3, function() { + test('`_.' + methodName + '` should clone metadata for created functions', 3, function() { function greet(greeting, name) { return greeting + ' ' + name; } @@ -5933,6 +5925,14 @@ equal(par2(), isPartial ? 'hi barney' : 'barney hi'); equal(par3(), isPartial ? 'hi pebbles' : 'pebbles hi'); }); + + test('`_.' + methodName + '` should work with curried methods', 2, function() { + var fn = function(a, b, c) { return a + b + c; }, + curried = _.curry(func(fn, 1)); + + equal(curried(2, 3), 6); + equal(curried(2)(3), 6); + }); }); /*--------------------------------------------------------------------------*/