Skip to content

Commit

Permalink
lodash: Add native method overwrite detection and optimize bind for…
Browse files Browse the repository at this point in the history
… native `bind`. [jddalton]
  • Loading branch information
jdalton committed May 6, 2012
1 parent 36ca2fb commit 2416dde
Show file tree
Hide file tree
Showing 3 changed files with 59 additions and 9 deletions.
45 changes: 37 additions & 8 deletions lodash.js
Expand Up @@ -29,6 +29,9 @@
/** Used to restore the original `_` reference in `noConflict` */
var oldDash = window._;

/** Used to detect if a method is native */
var reNative = /\{\s*\[native code\]\s*\}/;

/** Used to match tokens in template text */
var reToken = /__token__(\d+)/g;

Expand Down Expand Up @@ -65,10 +68,14 @@
slice = ArrayProto.slice,
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 */
var nativeIsArray = Array.isArray,
var nativeIsArray = reNative.test(nativeIsArray = Array.isArray) && nativeIsArray,
nativeIsFinite = window.isFinite,
nativeKeys = Object.keys;
nativeKeys = reNative.test(nativeKeys = Object.keys) && nativeKeys;

/** Timer shortcuts */
var clearTimeout = window.clearTimeout,
Expand Down Expand Up @@ -1548,19 +1555,41 @@
* // => 'hi moe!'
*/
function bind(func, thisArg) {
var args = slice.call(arguments, 2),
argsLength = args.length,
var methodName,
isFunc = toString.call(func) == funcClass;

// juggle arguments
if (!isFunc) {
var methodName = thisArg;
methodName = thisArg;
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() {
push.apply(args, arguments);
var result = (isFunc ? func : thisArg[methodName]).apply(thisArg, args);
args.length = argsLength;
var result,
args = arguments;

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;
};
}
Expand Down
15 changes: 14 additions & 1 deletion test/index.html
Expand Up @@ -12,7 +12,20 @@ <h2 id="qunit-banner"></h2>
<h2 id="qunit-userAgent"></h2>
<ol id="qunit-tests"></ol>
<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="../vendor/requirejs/require.js"></script>
<script>
Expand Down
8 changes: 8 additions & 0 deletions test/test.js
Expand Up @@ -62,6 +62,14 @@
skipTest(1)
}
});

test('avoids overwritten native methods', function() {
if (window.lodashBadKeys) {
notDeepEqual(lodashBadKeys.keys({ 'a': 1 }), []);
} else {
skipTest(1);
}
});
}());

/*--------------------------------------------------------------------------*/
Expand Down

0 comments on commit 2416dde

Please sign in to comment.