Permalink
Browse files

Fixing $.proxy to work like (and use) Function.prototype.bind (ticket…

  • Loading branch information...
gf3 committed Dec 15, 2010
1 parent c9c9057 commit 9f8cd6c499844451468257140e71f611abb3a040
Showing with 65 additions and 39 deletions.
  1. +30 −19 src/core.js
  2. +27 −17 src/event.js
  3. +8 −3 test/unit/core.js
View
@@ -740,31 +740,42 @@ jQuery.extend({
// A global GUID counter for objects
guid: 1,
- proxy: function( fn, proxy, thisObject ) {
- if ( arguments.length === 2 ) {
- if ( typeof proxy === "string" ) {
- thisObject = fn;
- fn = thisObject[ proxy ];
- proxy = undefined;
-
- } else if ( proxy && !jQuery.isFunction( proxy ) ) {
- thisObject = proxy;
- proxy = undefined;
- }
+ // Bind a function to a context, optionally partially applying any
+ // arguments.
+ proxy: function( fn, context ) {
+ var args, proxy;
+
+ // Quick check to determine if target is callable, in the spec
+ // this throws a TypeError, but we will just return undefined.
+ if ( ! jQuery.isFunction( fn ) ) {
+ return undefined;
}
- if ( !proxy && fn ) {
- proxy = function() {
- return fn.apply( thisObject || this, arguments );
- };
+ if ( jQuery.isFunction( Function.prototype.bind ) ) {

This comment has been minimized.

Show comment
Hide comment
@ajpiano

ajpiano Dec 15, 2010

We should probably drop a test for this into jQuery.support, testing for existence on every $.proxy call seems a waste of time....

@ajpiano

ajpiano Dec 15, 2010

We should probably drop a test for this into jQuery.support, testing for existence on every $.proxy call seems a waste of time....

This comment has been minimized.

Show comment
Hide comment
@gf3

gf3 Dec 15, 2010

Owner

Added in: 5b1b578, good call.

@gf3

gf3 Dec 15, 2010

Owner

Added in: 5b1b578, good call.

+ // Native bind
+ args = slice.call( arguments, 1 );
+ proxy = Function.prototype.bind.apply( fn, args );

This comment has been minimized.

Show comment
Hide comment
@fearphage

fearphage Dec 18, 2010

Why not fork here like the simulated bind based on args.length? call is still faster than apply.

@fearphage

fearphage Dec 18, 2010

Why not fork here like the simulated bind based on args.length? call is still faster than apply.

This comment has been minimized.

Show comment
Hide comment
@gf3

gf3 Dec 19, 2010

Owner

Good call. BAM: 6bc9fc7.

@gf3

gf3 Dec 19, 2010

Owner

Good call. BAM: 6bc9fc7.

+ } else {
+ // Simulated bind
+ args = slice.call( arguments, 2 );
+ if ( args.length ) {
+ proxy = function() {
+ return arguments.length ?
+ fn.apply( context, args.concat( slice.call( arguments ) ) ) :
+ fn.apply( context, args );
+ };
+ } else {
+ proxy = function() {
+ return arguments.length ?
+ fn.apply( context, arguments ) :
+ fn.call( context );
+ };
+ }
}
// Set the guid of unique handler to the same of original handler, so it can be removed
- if ( fn ) {
- proxy.guid = fn.guid = fn.guid || proxy.guid || jQuery.guid++;
- }
+ proxy.guid = fn.guid = fn.guid || proxy.guid || jQuery.guid++;
- // So proxy can be declared as an argument
return proxy;
},
View
@@ -891,6 +891,8 @@ if ( document.addEventListener ) {
jQuery.each(["bind", "one"], function( i, name ) {
jQuery.fn[ name ] = function( type, data, fn ) {
+ var handler;
+
// Handle object literals
if ( typeof type === "object" ) {
for ( var key in type ) {
@@ -904,10 +906,15 @@ jQuery.each(["bind", "one"], function( i, name ) {
data = undefined;
}
- var handler = name === "one" ? jQuery.proxy( fn, function( event ) {
- jQuery( this ).unbind( event, handler );
- return fn.apply( this, arguments );
- }) : fn;
+ if ( name === "one" ) {
+ handler = function( event ) {
+ jQuery( this ).unbind( event, handler );
+ return fn.apply( this, arguments );
+ };
+ handler.guid = fn.guid || jQuery.guid++;
+ } else {
+ handler = fn;
+ }
if ( type === "unload" && name !== "one" ) {
this.one( type, data, fn );
@@ -971,24 +978,27 @@ jQuery.fn.extend({
toggle: function( fn ) {
// Save reference to arguments for access in closure
var args = arguments,
- i = 1;
+ guid = fn.guid || jQuery.guid++,
+ i = 0,
+ toggler = function( event ) {
+ // Figure out which function to execute
+ var lastToggle = ( jQuery.data( this, "lastToggle" + fn.guid ) || 0 ) % i;
+ jQuery.data( this, "lastToggle" + fn.guid, lastToggle + 1 );
+
+ // Make sure that clicks stop
+ event.preventDefault();
+
+ // and execute the function
+ return args[ lastToggle ].apply( this, arguments ) || false;
+ };
// link all the functions, so any of them can unbind this click handler
+ toggler.guid = guid;
while ( i < args.length ) {
- jQuery.proxy( fn, args[ i++ ] );
+ args[ i++ ].guid = guid;
}
- return this.click( jQuery.proxy( fn, function( event ) {
- // Figure out which function to execute
- var lastToggle = ( jQuery.data( this, "lastToggle" + fn.guid ) || 0 ) % i;
- jQuery.data( this, "lastToggle" + fn.guid, lastToggle + 1 );
-
- // Make sure that clicks stop
- event.preventDefault();
-
- // and execute the function
- return args[ lastToggle ].apply( this, arguments ) || false;
- }));
+ return this.click( toggler );
},
hover: function( fnOver, fnOut ) {
View
@@ -858,7 +858,7 @@ test("jQuery.isEmptyObject", function(){
});
test("jQuery.proxy", function(){
- expect(4);
+ expect(5);
var test = function(){ equals( this, thisObject, "Make sure that scope is set properly." ); };
var thisObject = { foo: "bar", method: test };
@@ -872,8 +872,13 @@ test("jQuery.proxy", function(){
// Make sure it doesn't freak out
equals( jQuery.proxy( null, thisObject ), undefined, "Make sure no function was returned." );
- // Use the string shortcut
- jQuery.proxy( thisObject, "method" )();
+ // Partial application
+ var test2 = function( a ){ equals( a, "pre-applied", "Ensure arguments can be pre-applied." ); };
+ jQuery.proxy( test2, null, "pre-applied" )();
+
+ // Partial application w/ normal arguments
+ var test3 = function( a, b ){ equals( b, "normal", "Ensure arguments can be pre-applied and passed as usual." ); };
+ jQuery.proxy( test3, null, "pre-applied" )( "normal" );
});
test("jQuery.parseJSON", function(){

0 comments on commit 9f8cd6c

Please sign in to comment.