Drop funcDecomp optimization in baseCallback and ensure shortcut fusion for _.flow and _.flowRight works in minified builds.

This commit is contained in:
jdalton
2015-03-19 08:37:22 -07:00
parent 462c482873
commit 94ca50883f
2 changed files with 67 additions and 98 deletions

View File

@@ -120,9 +120,6 @@
var reRegExpChars = /[.*+?^${}()|[\]\/\\]/g,
reHasRegExpChars = RegExp(reRegExpChars.source);
/** Used to detect functions containing a `this` reference. */
var reThis = /\bthis\b/;
/** Used to match unescaped characters in compiled string literals. */
var reUnescapedString = /['\n\r\u2028\u2029\\]/g;
@@ -153,7 +150,7 @@
'Object', 'RegExp', 'Set', 'String', '_', 'clearTimeout', 'document',
'isFinite', 'parseInt', 'setTimeout', 'TypeError', 'Uint8Array',
'Uint8ClampedArray', 'Uint16Array', 'Uint32Array', 'WeakMap',
'window', 'WinRTError'
'window'
];
/** Used to fix the JScript `[[DontEnum]]` bug. */
@@ -831,6 +828,9 @@
/** Used to store function metadata. */
var metaMap = WeakMap && new WeakMap;
/** Used to lookup unminified function names. */
var realNames = {};
/** Used to lookup a type array constructors by `toStringTag`. */
var ctorByTag = {};
ctorByTag[float32Tag] = context.Float32Array;
@@ -1046,7 +1046,7 @@
* @memberOf _.support
* @type boolean
*/
support.funcDecomp = !isNative(context.WinRTError) && reThis.test(runInContext);
support.funcDecomp = /\bthis\b/.test(function() { return this; });
/**
* Detect if `Function#name` is supported (all but IE).
@@ -1846,9 +1846,9 @@
function baseCallback(func, thisArg, argCount) {
var type = typeof func;
if (type == 'function') {
return (typeof thisArg != 'undefined' && isBindable(func))
? bindCallback(func, thisArg, argCount)
: func;
return typeof thisArg == 'undefined'
? func
: bindCallback(func, thisArg, argCount);
}
if (func == null) {
return identity;
@@ -3379,29 +3379,37 @@
if (!length) {
return function() { return arguments[0]; };
}
var index = fromRight ? length : -1,
var wrapper,
index = fromRight ? length : -1,
leftIndex = 0,
funcs = Array(length),
wrapper = new LodashWrapper([]);
funcs = Array(length);
while ((fromRight ? index-- : ++index < length)) {
var func = arguments[index];
var func = funcs[leftIndex++] = arguments[index];
if (typeof func != 'function') {
throw new TypeError(FUNC_ERROR_TEXT);
}
var data = func.name === 'wrapper' && getData(func);
if (data && data !== true && isLaziable(data[0])) {
wrapper = wrapper[data[0].name].apply(wrapper, data[3]);
} else if (func.length == 1 && isLaziable(func)) {
wrapper = wrapper[func.name]();
} else {
wrapper = wrapper.thru(func);
var funcName = wrapper ? null : getFuncName(func);
if (funcName && (funcName == 'wrapper' || isLaziable(func))) {
wrapper = new LodashWrapper([]);
}
}
index = wrapper ? -1 : length;
while (++index < length) {
func = funcs[index];
var funcName = getFuncName(func),
data = funcName == 'wrapper' ? getData(func) : null;
if (data && isLaziable(data[0])) {
wrapper = wrapper[getFuncName(data[0])].apply(wrapper, data[3]);
} else {
wrapper = (func.length == 1 && isLaziable(func)) ? wrapper[funcName]() : wrapper.thru(func);
}
funcs[leftIndex++] = func;
}
return function() {
var args = arguments;
if (args.length == 1 && isArray(args[0])) {
if (wrapper && args.length == 1 && isArray(args[0])) {
return wrapper.plant(args[0]).value();
}
var index = 0,
@@ -3699,10 +3707,10 @@
partials = holders = null;
}
var data = !isBindKey && getData(func),
var data = isBindKey ? null : getData(func),
newData = [func, bitmask, thisArg, partials, holders, partialsRight, holdersRight, argPos, ary, arity];
if (data && data !== true) {
if (data) {
mergeData(newData, data);
bitmask = newData[1];
arity = newData[9];
@@ -4055,31 +4063,6 @@
return result;
}
/**
* Checks if `func` is eligible for `this` binding.
*
* @private
* @param {Function} func The function to check.
* @returns {boolean} Returns `true` if `func` is eligible, else `false`.
*/
function isBindable(func) {
var support = lodash.support,
result = !(support.funcNames ? func.name : support.funcDecomp);
if (!result) {
var source = fnToString.call(func);
if (!support.funcNames) {
result = !reFuncName.test(source);
}
if (!result) {
// Check if `func` references the `this` keyword and store the result.
result = reThis.test(source) || isNative(func);
baseSetData(func, result);
}
}
return result;
}
/**
* Checks if `value` is a valid array-like index.
*
@@ -4121,9 +4104,28 @@
return false;
}
var getFuncName = !support.funcNames ? constant('') : function(func) {
var result = func.name;
if (result) {
return realNames[result] || result;
}
var anons = realNames[result];
if (!anons) {
return result;
}
var length = anons.length;
while (length--) {
var anon = anons[length];
if (anon.func == func) {
return anon.name;
}
}
return result;
};
function isLaziable(func) {
var funcName = support.funcNames ? func.name : '';
return (funcName && func === lodash[funcName] && funcName in LazyWrapper.prototype) || false;
var funcName = getFuncName(func);
return funcName ? func === lodash[funcName] : false;
}
/**
@@ -11942,6 +11944,20 @@
};
});
if (support.funcNames) {
realNames[createHybridWrapper(null, BIND_KEY_FLAG).name] = 'wrapper';
baseForOwn(LazyWrapper.prototype, function(func, methodName) {
var lodashFunc = lodash[methodName],
key = lodashFunc.name;
if (key) {
realNames[key] = methodName;
} else {
var anons = realNames[key] || (realNames[key] = []);
anons.push({ 'name': methodName, 'func': lodashFunc });
}
});
}
// Add functions to the lazy wrapper.
LazyWrapper.prototype.clone = lazyClone;
LazyWrapper.prototype.reverse = lazyReverse;

View File

@@ -2233,7 +2233,7 @@
});
test('`_.' + methodName + '` should support shortcut fusion', 3, function() {
if (!isNpm) {
if (!isNpm && _.support.funcNames) {
var filterCount = 0,
mapCount = 0;
@@ -2625,53 +2625,6 @@
}
});
test('should return the function provided when there is no `this` reference', 2, function() {
function a() {}
function b() { return this.b; }
var object = {};
if (_.support.funcDecomp) {
strictEqual(_.callback(a, object), a);
notStrictEqual(_.callback(b, object), b);
}
else {
skipTest(2);
}
});
test('should work with bizarro `_.support.funcNames`', 6, function() {
function a() {}
var b = function() {};
function c() {
return this;
}
var object = {},
supportBizarro = lodashBizarro ? lodashBizarro.support : {},
funcDecomp = supportBizarro.funcDecomp,
funcNames = supportBizarro.funcNames;
supportBizarro.funcNames = !supportBizarro.funcNames;
supportBizarro.funcDecomp = true;
_.each([a, b, c], function(fn) {
if (lodashBizarro && _.support.funcDecomp) {
var callback = lodashBizarro.callback(fn, object);
strictEqual(callback(), fn === c ? object : undefined);
strictEqual(callback === fn, _.support.funcNames && fn === a);
}
else {
skipTest(2);
}
});
supportBizarro.funcDecomp = funcDecomp;
supportBizarro.funcNames = funcNames;
});
test('should work as an iteratee for methods like `_.map`', 1, function() {
var fn = function() { return this instanceof Number; },
array = [fn, fn, fn],