From 2e5de88445a32e2f124697e51cbe81d1a98987ac Mon Sep 17 00:00:00 2001 From: John-David Dalton Date: Sun, 21 Jul 2013 23:46:59 -0700 Subject: [PATCH] Remove `hasThis` and add comments to `createBound` and `createCallback`. Former-commit-id: cdc9a69dc60913d6c2383053a881453dd2b7b0d5 --- build.js | 24 +++++++++++++---------- lodash.js | 57 ++++++++++++++++++++++++++++++------------------------- 2 files changed, 45 insertions(+), 36 deletions(-) diff --git a/build.js b/build.js index bfc4a95e0..5f44ddb41 100644 --- a/build.js +++ b/build.js @@ -102,7 +102,7 @@ 'compose': [], 'contains': ['baseEach', 'getIndexOf', 'isString'], 'countBy': ['createCallback', 'forEach'], - 'createCallback': ['hasThis', 'identity', 'isEqual', 'isObject', 'keys'], + 'createCallback': ['bind', 'identity', 'isEqual', 'isObject', 'keys', 'setBindData'], 'debounce': ['isObject'], 'defaults': ['createCallback', 'createIterator'], 'defer': ['bind'], @@ -215,7 +215,6 @@ 'getArray': [], 'getIndexOf': ['baseIndexOf', 'indexOf'], 'getObject': [], - 'hasThis': ['setBindData'], 'isNode': [], 'iteratorTemplate': [], 'lodash': ['lodashWrapper'], @@ -539,7 +538,6 @@ 'escapeStringChar', 'getArray', 'getObject', - 'hasThis', 'isNode', 'iteratorTemplate', 'lodash', @@ -1638,17 +1636,22 @@ * @returns {String} Returns the modified source. */ function removeEsOptimization(source) { - // remove `__bindData` logic and `setBindData` function calls from `createBound` + // remove `__bindData__` logic and `setBindData` function calls from `createBound` source = source.replace(matchFunction(source, 'createBound'), function(match) { return match .replace(/\bargs *=.+?__bindData__.+\s+/, '') - .replace(/^( *)if *\(args\)[\s\S]+?\n\1}/m, '') - .replace(/^.+?setBindData.+\n/m, '') + .replace(/(?:\s*\/\/.*)*\n( *)if *\(args\)[\s\S]+?\n\1}/m, '') + .replace(/(?:\s*\/\/.*)*\n.+args *= *nativeSlice.+/, '') + .replace(/(?:\s*\/\/.*)*\n.+?setBindData.+/m, '') + }); - // remove `hasThis` use `_.createCallback` + // remove `__bindData__` logic and `setBindData` function calls from `_.createCallback` source = source.replace(matchFunction(source, 'createCallback'), function(match) { - return match.replace(/\s*\|\|\s*!hasThis\(.+?\)/, ''); + return match + .replace(/^( *)var bindData *=[\s\S]+?\n\1}\n/m, '') + .replace(/\s*\|\|\s*!bindData\b/, '') + .replace(/^( *)else if \(bindData[\s\S]+?\n\1}\n/m, '') }); return source; @@ -2732,8 +2735,9 @@ }); } if (isLegacy || isMobile || isUnderscore) { - funcDependencyMap.createBound = _.without(funcDependencyMap.createBound, 'setBindData'); - funcDependencyMap.createCallback = _.without(funcDependencyMap.createCallback, 'hasThis'); + _.each(['createBound', 'createCallback'], function(funcName) { + funcDependencyMap[funcName] = _.without(funcDependencyMap[funcName], 'bind', 'setBindData'); + }); } if (_.contains(plusFuncs, 'chain') == !isUnderscore) { funcDependencyMap.mixin = _.without(funcDependencyMap.mixin, 'isFunction'); diff --git a/lodash.js b/lodash.js index f2ca6fc9b..be48aaa5c 100644 --- a/lodash.js +++ b/lodash.js @@ -1386,10 +1386,11 @@ * @returns {Function} Returns the new bound function. */ function createBound(func, thisArg, partialArgs, partialRightArgs, isPartial, isAlt) { - var isFunc = isFunction(func); + var isBindKey = isAlt && !isPartial, + isFunc = isFunction(func); - // except for `_.bindKey`, throw when `func` is not a function - if (!isFunc && (isPartial || !isAlt)) { + // throw if `func` is not a function when not behaving as `_.bindKey` + if (!isFunc && !isBindKey) { throw new TypeError; } var args = func.__bindData__, @@ -1398,19 +1399,25 @@ if (args) { push.apply(args[2], partialArgs); push.apply(args[3], partialRightArgs); + + // add `thisArg` to previous `_.partial` and `_.partialRight` arguments if (!isPartial && args[4]) { args[1] = thisArg; args[4] = false; + args[5] = isAlt; } return createBound.apply(null, args); } - // juggle arguments for `_.bindKey` - if (!isPartial && isAlt) { + // take a snapshot of `arguments` before juggling + args = nativeSlice.call(arguments); + + // juggle arguments for `_.bindKey` behavior + if (isBindKey) { thisArg = func; } // use `Function#bind` if it exists and is fast // (in V8 `Function#bind` is slower except when partially applied) - if (!isPartial && !isAlt && (support.fastBind || (nativeBind && partialArgs.length))) { + if (!isPartial && !isAlt && !partialRightArgs.length && (support.fastBind || (nativeBind && partialArgs.length))) { var bound = nativeBind.call.apply(nativeBind, concat.call(arrayRef, func, thisArg, partialArgs)); } else { @@ -1420,7 +1427,7 @@ var args = arguments, thisBinding = isPartial ? this : thisArg; - if (!isFunc) { + if (isBindKey) { func = thisArg[key]; } if (partialArgs.length || partialRightArgs.length) { @@ -1439,7 +1446,7 @@ return func.apply(thisBinding, args); }; } - setBindData(bound, nativeSlice.call(arguments)); + setBindData(bound, args); return bound; } @@ -1543,23 +1550,6 @@ return result; } - /** - * Checks if `func` references the `this` keyword. - * - * @private - * @param {Function} func The function to inspect. - * @returns {Boolean} Returns `true` if `this` is referenced, else `false`. - */ - function hasThis(func) { - var result = func.__bindData__; - if (typeof result != 'undefined') { - return result === true || (result && result[4]); - } - result = !reThis || reThis.test(fnToString.call(func)); - setBindData(func, result); - return result; - } - /** * Sets `this` binding data on a given function. * @@ -4828,6 +4818,7 @@ } var type = typeof func; if (type != 'function') { + // handle "_.pluck" style callback shorthands if (type != 'object') { return function(object) { return object[func]; @@ -4837,7 +4828,10 @@ key = props[0], a = func[key]; + // handle "_.where" style callback shorthands if (props.length == 1 && a === a && !isObject(a)) { + // fast path the common case of passing an object with a single + // property containing a primitive value return function(object) { var b = object[key]; return a === b && (a !== 0 || (1 / a == 1 / b)); @@ -4855,9 +4849,20 @@ return result; }; } - if (typeof thisArg == 'undefined' || !hasThis(func)) { + var bindData = func.__bindData__; + if (typeof bindData == 'undefined') { + // checks if `func` references the `this` keyword and stores the result + bindData = !reThis || reThis.test(fnToString.call(func)); + setBindData(func, bindData); + } + if (typeof thisArg == 'undefined' || !bindData) { return func; } + else if (bindData !== true) { + // exit early if already bound or leverage bind optimizations if + // created by `_.partial` or `_.partialRight` + return bindData[4] ? bind(func, thisArg) : func; + } if (argCount === 1) { return function(value) { return func.call(thisArg, value);