lodash: Add native method overwrite detection and optimize bind for native bind. [jddalton]

Former-commit-id: d968957e494fb828df155d2f9b0d3faf24e38b5e
This commit is contained in:
John-David Dalton
2012-05-06 17:55:23 -04:00
parent 6d3d5f77bc
commit 14c8863657
3 changed files with 59 additions and 9 deletions

View File

@@ -29,6 +29,9 @@
/** Used to restore the original `_` reference in `noConflict` */ /** Used to restore the original `_` reference in `noConflict` */
var oldDash = window._; var oldDash = window._;
/** Used to detect if a method is native */
var reNative = /\{\s*\[native code\]\s*\}/;
/** Used to match tokens in template text */ /** Used to match tokens in template text */
var reToken = /__token__(\d+)/g; var reToken = /__token__(\d+)/g;
@@ -65,10 +68,14 @@
slice = ArrayProto.slice, slice = ArrayProto.slice,
toString = ObjectProto.toString; toString = ObjectProto.toString;
/* Used if `Function#bind` exists and is inferred to be fast (i.e. all but V8) */
var nativeBind = reNative.test(nativeBind = slice.bind) &&
(/\n/.test(nativeBind) || toString.call(window.opera) == '[object Opera]') && nativeBind;
/* Native method shortcuts for methods with the same name as other `lodash` methods */ /* Native method shortcuts for methods with the same name as other `lodash` methods */
var nativeIsArray = Array.isArray, var nativeIsArray = reNative.test(nativeIsArray = Array.isArray) && nativeIsArray,
nativeIsFinite = window.isFinite, nativeIsFinite = window.isFinite,
nativeKeys = Object.keys; nativeKeys = reNative.test(nativeKeys = Object.keys) && nativeKeys;
/** Timer shortcuts */ /** Timer shortcuts */
var clearTimeout = window.clearTimeout, var clearTimeout = window.clearTimeout,
@@ -1548,19 +1555,41 @@
* // => 'hi moe!' * // => 'hi moe!'
*/ */
function bind(func, thisArg) { function bind(func, thisArg) {
var args = slice.call(arguments, 2), var methodName,
argsLength = args.length,
isFunc = toString.call(func) == funcClass; isFunc = toString.call(func) == funcClass;
// juggle arguments // juggle arguments
if (!isFunc) { if (!isFunc) {
var methodName = thisArg; methodName = thisArg;
thisArg = func; thisArg = func;
} }
// use native `Function#bind` if faster
else if (nativeBind) {
func = nativeBind.call.apply(nativeBind, arguments);
return function() {
return func.apply(undefined, arguments);
};
}
var partialArgs = slice.call(arguments, 2),
partialArgsLength = partialArgs.length;
return function() { return function() {
push.apply(args, arguments); var result,
var result = (isFunc ? func : thisArg[methodName]).apply(thisArg, args); args = arguments;
args.length = argsLength;
if (!isFunc) {
func = thisArg[methodName];
}
if (partialArgsLength) {
if (args.length) {
partialArgs.length = partialArgsLength;
push.apply(partialArgs, args);
}
args = partialArgs;
}
result = args.length ? func.apply(thisArg, args) : func.call(thisArg);
partialArgs.length = partialArgsLength;
return result; return result;
}; };
} }

View File

@@ -12,7 +12,20 @@
<h2 id="qunit-userAgent"></h2> <h2 id="qunit-userAgent"></h2>
<ol id="qunit-tests"></ol> <ol id="qunit-tests"></ol>
<script src="../vendor/qunit/qunit/qunit.js"></script> <script src="../vendor/qunit/qunit/qunit.js"></script>
<script>var _2, _3, _ = 1;</script> <script>
var _2,
_3 = Object.keys;
Object.keys = function() { return []; };
</script>
<script src="../lodash.js"></script>
<script>
var lodashBadKeys = _,
_ = 1;
Object.keys = _3;
_3 = void 0;
</script>
<script src="../lodash.js"></script> <script src="../lodash.js"></script>
<script src="../vendor/requirejs/require.js"></script> <script src="../vendor/requirejs/require.js"></script>
<script> <script>

View File

@@ -62,6 +62,14 @@
skipTest(1) skipTest(1)
} }
}); });
test('avoids overwritten native methods', function() {
if (window.lodashBadKeys) {
notDeepEqual(lodashBadKeys.keys({ 'a': 1 }), []);
} else {
skipTest(1);
}
});
}()); }());
/*--------------------------------------------------------------------------*/ /*--------------------------------------------------------------------------*/