From 3a8e44745c014871bc56e94d91e57c45ae4be208 Mon Sep 17 00:00:00 2001 From: Timmy Willison <4timmywil@gmail.com> Date: Mon, 11 Dec 2017 12:58:35 -0500 Subject: [PATCH] Core: deprecate jQuery.proxy (not slated for removal) Fixes gh-2958 Close gh-3885 --- src/core.js | 29 ------------------------- src/deprecated.js | 36 +++++++++++++++++++++++++++++-- src/effects.js | 2 +- test/unit/core.js | 48 ----------------------------------------- test/unit/deprecated.js | 48 +++++++++++++++++++++++++++++++++++++++++ test/unit/event.js | 15 +++++++------ 6 files changed, 91 insertions(+), 87 deletions(-) diff --git a/src/core.js b/src/core.js index 06d0e83ad..037e31740 100644 --- a/src/core.js +++ b/src/core.js @@ -395,35 +395,6 @@ jQuery.extend( { // A global GUID counter for objects guid: 1, - // Bind a function to a context, optionally partially applying any - // arguments. - proxy: function( fn, context ) { - var tmp, args, proxy; - - if ( typeof context === "string" ) { - tmp = fn[ context ]; - context = fn; - fn = tmp; - } - - // 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; - } - - // Simulated bind - args = slice.call( arguments, 2 ); - proxy = function() { - return fn.apply( context || this, args.concat( slice.call( arguments ) ) ); - }; - - // Set the guid of unique handler to the same of original handler, so it can be removed - proxy.guid = fn.guid = fn.guid || jQuery.guid++; - - return proxy; - }, - // jQuery.support is not used in Core but other projects attach their // properties to it so it needs to exist. support: support diff --git a/src/deprecated.js b/src/deprecated.js index 74907a289..8523ee5b8 100644 --- a/src/deprecated.js +++ b/src/deprecated.js @@ -2,8 +2,9 @@ define( [ "./core", "./core/nodeName", "./core/camelCase", - "./var/isWindow" -], function( jQuery, nodeName, camelCase, isWindow ) { + "./var/isWindow", + "./var/slice" +], function( jQuery, nodeName, camelCase, isWindow, slice ) { "use strict"; @@ -28,6 +29,37 @@ jQuery.fn.extend( { } } ); +// Bind a function to a context, optionally partially applying any +// arguments. +// jQuery.proxy is deprecated to promote standards (specifically Function#bind) +// However, it is not slated for removal any time soon +jQuery.proxy = function( fn, context ) { + var tmp, args, proxy; + + if ( typeof context === "string" ) { + tmp = fn[ context ]; + context = fn; + fn = tmp; + } + + // 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; + } + + // Simulated bind + args = slice.call( arguments, 2 ); + proxy = function() { + return fn.apply( context || this, args.concat( slice.call( arguments ) ) ); + }; + + // Set the guid of unique handler to the same of original handler, so it can be removed + proxy.guid = fn.guid = fn.guid || jQuery.guid++; + + return proxy; +}; + jQuery.holdReady = function( hold ) { if ( hold ) { jQuery.readyWait++; diff --git a/src/effects.js b/src/effects.js index ec82d59f6..514baaec8 100644 --- a/src/effects.js +++ b/src/effects.js @@ -387,7 +387,7 @@ function Animation( elem, properties, options ) { if ( result ) { if ( jQuery.isFunction( result.stop ) ) { jQuery._queueHooks( animation.elem, animation.opts.queue ).stop = - jQuery.proxy( result.stop, result ); + result.stop.bind( result ); } return result; } diff --git a/test/unit/core.js b/test/unit/core.js index 6778b14e9..7c7514ccc 100644 --- a/test/unit/core.js +++ b/test/unit/core.js @@ -1531,54 +1531,6 @@ QUnit.test( "jQuery.isEmptyObject", function( assert ) { // equal(true, jQuery.isEmptyObject(null), "isEmptyObject on null" ); } ); -QUnit.test( "jQuery.proxy", function( assert ) { - assert.expect( 9 ); - - var test2, test3, test4, fn, cb, - test = function() { - assert.equal( this, thisObject, "Make sure that scope is set properly." ); - }, - thisObject = { foo: "bar", method: test }; - - // Make sure normal works - test.call( thisObject ); - - // Basic scoping - jQuery.proxy( test, thisObject )(); - - // Another take on it - jQuery.proxy( thisObject, "method" )(); - - // Make sure it doesn't freak out - assert.equal( jQuery.proxy( null, thisObject ), undefined, "Make sure no function was returned." ); - - // Partial application - test2 = function( a ) { - assert.equal( a, "pre-applied", "Ensure arguments can be pre-applied." ); - }; - jQuery.proxy( test2, null, "pre-applied" )(); - - // Partial application w/ normal arguments - test3 = function( a, b ) { - assert.equal( b, "normal", "Ensure arguments can be pre-applied and passed as usual." ); - }; - jQuery.proxy( test3, null, "pre-applied" )( "normal" ); - - // Test old syntax - test4 = { "meth": function( a ) { - assert.equal( a, "boom", "Ensure old syntax works." ); - } }; - jQuery.proxy( test4, "meth" )( "boom" ); - - // jQuery 1.9 improved currying with `this` object - fn = function() { - assert.equal( Array.prototype.join.call( arguments, "," ), "arg1,arg2,arg3", "args passed" ); - assert.equal( this.foo, "bar", "this-object passed" ); - }; - cb = jQuery.proxy( fn, null, "arg1", "arg2" ); - cb.call( thisObject, "arg3" ); -} ); - QUnit.test( "jQuery.parseHTML", function( assert ) { assert.expect( 23 ); diff --git a/test/unit/deprecated.js b/test/unit/deprecated.js index 962cad4fa..9837b3bae 100644 --- a/test/unit/deprecated.js +++ b/test/unit/deprecated.js @@ -208,3 +208,51 @@ QUnit.test( "jQuery.now", function( assert ) { assert.ok( typeof jQuery.now() === "number", "jQuery.now is a function" ); } ); + +QUnit.test( "jQuery.proxy", function( assert ) { + assert.expect( 9 ); + + var test2, test3, test4, fn, cb, + test = function() { + assert.equal( this, thisObject, "Make sure that scope is set properly." ); + }, + thisObject = { foo: "bar", method: test }; + + // Make sure normal works + test.call( thisObject ); + + // Basic scoping + jQuery.proxy( test, thisObject )(); + + // Another take on it + jQuery.proxy( thisObject, "method" )(); + + // Make sure it doesn't freak out + assert.equal( jQuery.proxy( null, thisObject ), undefined, "Make sure no function was returned." ); + + // Partial application + test2 = function( a ) { + assert.equal( a, "pre-applied", "Ensure arguments can be pre-applied." ); + }; + jQuery.proxy( test2, null, "pre-applied" )(); + + // Partial application w/ normal arguments + test3 = function( a, b ) { + assert.equal( b, "normal", "Ensure arguments can be pre-applied and passed as usual." ); + }; + jQuery.proxy( test3, null, "pre-applied" )( "normal" ); + + // Test old syntax + test4 = { "meth": function( a ) { + assert.equal( a, "boom", "Ensure old syntax works." ); + } }; + jQuery.proxy( test4, "meth" )( "boom" ); + + // jQuery 1.9 improved currying with `this` object + fn = function() { + assert.equal( Array.prototype.join.call( arguments, "," ), "arg1,arg2,arg3", "args passed" ); + assert.equal( this.foo, "bar", "this-object passed" ); + }; + cb = jQuery.proxy( fn, null, "arg1", "arg2" ); + cb.call( thisObject, "arg3" ); +} ); diff --git a/test/unit/event.js b/test/unit/event.js index ccaf72514..e23c6fd29 100644 --- a/test/unit/event.js +++ b/test/unit/event.js @@ -694,8 +694,8 @@ QUnit.test( "on(), with different this object", function( assert ) { }; jQuery( "#firstp" ) - .on( "click", jQuery.proxy( handler1, thisObject ) ).trigger( "click" ).off( "click", handler1 ) - .on( "click", data, jQuery.proxy( handler2, thisObject ) ).trigger( "click" ).off( "click", handler2 ); + .on( "click", handler1.bind( thisObject ) ).trigger( "click" ).off( "click", handler1 ) + .on( "click", data, handler2.bind( thisObject ) ).trigger( "click" ).off( "click", handler2 ); assert.ok( !jQuery._data( jQuery( "#firstp" )[ 0 ], "events" ), "Event handler unbound when using different this object and data." ); } ); @@ -1640,18 +1640,19 @@ QUnit.test( ".on()/.off()", function( assert ) { jQuery( "#body" ).off( "click", "#foo" ); // Test binding with different this object - jQuery( "#body" ).on( "click", "#foo", jQuery.proxy( function() { - assert.equal( this.foo, "bar", "on with event scope" ); }, { "foo": "bar" } - ) ); + jQuery( "#body" ).on( "click", "#foo", function() { + assert.equal( this.foo, "bar", "on with event scope" ); + }.bind( { "foo": "bar" } ) ); + jQuery( "#foo" ).trigger( "click" ); jQuery( "#body" ).off( "click", "#foo" ); // Test binding with different this object, event data, and trigger data - jQuery( "#body" ).on( "click", "#foo", true, jQuery.proxy( function( e, data ) { + jQuery( "#body" ).on( "click", "#foo", true, function( e, data ) { assert.equal( e.data, true, "on with with different this object, event data, and trigger data" ); assert.equal( this.foo, "bar", "on with with different this object, event data, and trigger data" ); assert.equal( data, true, "on with with different this object, event data, and trigger data" ); - }, { "foo": "bar" } ) ); + }.bind( { "foo": "bar" } ) ); jQuery( "#foo" ).trigger( "click", true ); jQuery( "#body" ).off( "click", "#foo" );