From 49c032315c003373774d25bc9728db26b594267c Mon Sep 17 00:00:00 2001 From: John-David Dalton Date: Thu, 18 Jul 2013 08:58:40 -0700 Subject: [PATCH] Add `hasThis` to avoid repeatedly checking for `this`. Former-commit-id: 15ce8566364ddac60ac01f3a36343c33d9739b77 --- build.js | 40 +++++++++++++++++++++++++++++++++------- lodash.js | 30 +++++++++++++++++++++++++++++- 2 files changed, 62 insertions(+), 8 deletions(-) diff --git a/build.js b/build.js index 84357014b..78a0dd427 100644 --- a/build.js +++ b/build.js @@ -1360,6 +1360,18 @@ return result ? result[0] : ''; } + /** + * Gets the `hasThis` fork from `source`. + * + * @private + * @param {String} source The source to inspect. + * @returns {String} Returns the `isFunction` fork. + */ + function getHasThisFork(source) { + var result = source.match(/(?:\s*\/\/.*)*\n( *)if *\(!defineProperty\s*\|\|\s*!reThis\)[\s\S]+?\n\1}/); + return result ? result[0] : ''; + } + /** * Gets the Lo-Dash method assignments snippet from `source`. * @@ -1784,21 +1796,34 @@ } /** - * Removes the binding optimization from `source`. + * Removes `hasThis` and its binding optimizations from `source`. * * @private * @param {String} source The source to process. * @returns {String} Returns the modified source. */ - function removeBindingOptimization(source) { - // remove `reThis` from `createCallback` + function removeHasThis(source) { + source = removeFunction(source, 'hasThis'); + + // remove `hasThis` use `_.createCallback` source = source.replace(matchFunction(source, 'createCallback'), function(match) { - return match.replace(/\s*\|\|\s*\(reThis[\s\S]+?\)\)\)/, ''); + return match.replace(/\s*\|\|\s*!hasThis\(.+?\)/, ''); }); return source; } + /** + * Removes the `hasThis` fork from `source`. + * + * @private + * @param {String} source The source to process. + * @returns {String} Returns the modified source. + */ + function removeHasThisFork(source) { + return source.replace(getHasThisFork(source), ''); + } + /** * Removes the `Object.keys` object iteration optimization from `source`. * @@ -2982,8 +3007,9 @@ ); } if (isModern) { - source = removeSupportSpliceObjects(source); source = removeIsArgumentsFork(source); + source = removeHasThisFork(source); + source = removeSupportSpliceObjects(source); if (isMobile) { source = replaceSupportProp(source, 'enumPrototypes', 'true'); @@ -3001,7 +3027,7 @@ } } if ((isLegacy || isMobile || isUnderscore) && !isLodash('createCallback')) { - source = removeBindingOptimization(source); + source = removeHasThis(source); } if (isLegacy || isMobile || isUnderscore) { if (isMobile || (!isLodash('assign') && !isLodash('defaults') && !isLodash('forIn') && !isLodash('forOwn'))) { @@ -4080,7 +4106,7 @@ }); // remove forks of removed functions - _.each(['createObject', 'defer', 'isArguments', 'isArray', 'isFunction'], function(funcName) { + _.each(['createObject', 'defer', 'hasThis', 'isArguments', 'isArray', 'isFunction'], function(funcName) { if (isExcluded(funcName)) { source = eval('remove' + capitalize(funcName) + 'Fork')(source); } diff --git a/lodash.js b/lodash.js index 08056ca63..3d3ab18fe 100644 --- a/lodash.js +++ b/lodash.js @@ -1204,6 +1204,34 @@ 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.__hasThis__; + if (typeof result == 'boolean') { + return result; + } + result = reThis.test(fnToString.call(func)); + defineProperty(func, '__hasThis__', { + 'configurable': true, + 'enumerable': false, + 'writable': true, + 'value': result + }); + return result; + } + // fallback for older browsers + if (!defineProperty || !reThis) { + hasThis = function() { + return true; + }; + } + /** * A fallback implementation of `isPlainObject` which checks if a given `value` * is an object created by the `Object` constructor, assuming objects created @@ -4784,7 +4812,7 @@ return result; }; } - if (typeof thisArg == 'undefined' || (reThis && !reThis.test(fnToString.call(func)))) { + if (typeof thisArg == 'undefined' || !hasThis(func)) { return func; } if (argCount === 1) {