]> source.dussan.org Git - jquery.git/commitdiff
Deferred: Remove default callback context
authorRichard Gibson <richard.gibson@gmail.com>
Fri, 15 Apr 2016 21:13:59 +0000 (17:13 -0400)
committerRichard Gibson <richard.gibson@gmail.com>
Sat, 23 Apr 2016 04:30:48 +0000 (00:30 -0400)
Employs strict mode to simplify Deferred callback context handling.

Fixes gh-3060
Closes gh-3061

build/tasks/build.js
src/deferred.js
src/wrapper.js
test/unit/deferred.js

index a62f04ab194a23989cfe31d024b8e35f73c624c0..0eff368f79879d66cf85638051dec7a75b32eaf8 100644 (file)
@@ -23,6 +23,9 @@ module.exports = function( grunt ) {
                        baseUrl: "src",
                        name: "jquery",
 
+                       // Allow strict mode
+                       useStrict: true,
+
                        // We have multiple minify steps
                        optimize: "none",
 
index bc2ea863779cfcc2fd7766d8c68b22723d608b46..91c55a64e5fd21649c113ac34c5d9a24bb57c0b8 100644 (file)
@@ -4,6 +4,8 @@ define( [
        "./callbacks"
 ], function( jQuery, slice ) {
 
+"use strict";
+
 function Identity( v ) {
        return v;
 }
@@ -60,7 +62,7 @@ jQuery.extend( {
                                                                                .fail( newDefer.reject );
                                                                } else {
                                                                        newDefer[ tuple[ 0 ] + "With" ](
-                                                                               this === promise ? newDefer.promise() : this,
+                                                                               this,
                                                                                fn ? [ returned ] : arguments
                                                                        );
                                                                }
@@ -73,7 +75,7 @@ jQuery.extend( {
                                        var maxDepth = 0;
                                        function resolve( depth, deferred, handler, special ) {
                                                return function() {
-                                                       var that = this === promise ? undefined : this,
+                                                       var that = this,
                                                                args = arguments,
                                                                mightThrow = function() {
                                                                        var returned, then;
@@ -144,8 +146,7 @@ jQuery.extend( {
 
                                                                                // Process the value(s)
                                                                                // Default process is resolve
-                                                                               ( special || deferred.resolveWith )(
-                                                                                       that || deferred.promise(), args );
+                                                                               ( special || deferred.resolveWith )( that, args );
                                                                        }
                                                                },
 
@@ -174,8 +175,7 @@ jQuery.extend( {
                                                                                                        args = [ e ];
                                                                                                }
 
-                                                                                               deferred.rejectWith( that || deferred.promise(),
-                                                                                                       args );
+                                                                                               deferred.rejectWith( that, args );
                                                                                        }
                                                                                }
                                                                        };
@@ -282,7 +282,7 @@ jQuery.extend( {
                        // deferred.resolve = function() { deferred.resolveWith(...) }
                        // deferred.reject = function() { deferred.rejectWith(...) }
                        deferred[ tuple[ 0 ] ] = function() {
-                               deferred[ tuple[ 0 ] + "With" ]( this === deferred ? promise : this, arguments );
+                               deferred[ tuple[ 0 ] + "With" ]( this === deferred ? undefined : this, arguments );
                                return this;
                        };
 
index 4ad7776d1b7ddb70bc6ff53a2e783239fb784a9d..8866d6bcd17ca78c197a5faba4098203e268d9ad 100644 (file)
 // Pass this if window is not defined yet
 }( typeof window !== "undefined" ? window : this, function( window, noGlobal ) {
 
-// Support: Firefox 18+
-// Can't be in strict mode, several libs including ASP.NET trace
-// the stack via arguments.caller.callee and Firefox dies if
-// you try to trace through "use strict" call chains. (#13335)
-//"use strict";
+// Edge <= 12 - 13+, Firefox <=18 - 45+, IE 10 - 11, Safari 5.1 - 9+, iOS 6 - 9.1
+// throw exceptions when non-strict code (e.g., ASP.NET 4.5) accesses strict mode
+// arguments.callee.caller (trac-13335). But as of jQuery 3.0 (2016), strict mode should be common
+// enough that all such attempts are guarded in a try block.
+"use strict";
 
 // @CODE
 // build.js inserts compiled jQuery here
index 3854f225fd5452308068027d45c34074122b8690..af237acf4c954c4e2ad052593705d33d22a0935c 100644 (file)
@@ -16,20 +16,21 @@ jQuery.each( [ "", " - new operator" ], function( _, withNew ) {
 
                assert.ok( jQuery.isFunction( defer.pipe ), "defer.pipe is a function" );
 
-               createDeferred().resolve().done( function() {
+               defer.resolve().done( function() {
                        assert.ok( true, "Success on resolve" );
-                       assert.strictEqual( this.state(), "resolved", "Deferred is resolved (state)" );
+                       assert.strictEqual( defer.state(), "resolved", "Deferred is resolved (state)" );
                } ).fail( function() {
                        assert.ok( false, "Error on resolve" );
                } ).always( function() {
                        assert.ok( true, "Always callback on resolve" );
                } );
 
-               createDeferred().reject().done( function() {
+               defer = createDeferred();
+               defer.reject().done( function() {
                        assert.ok( false, "Success on reject" );
                } ).fail( function() {
                        assert.ok( true, "Error on reject" );
-                       assert.strictEqual( this.state(), "rejected", "Deferred is rejected (state)" );
+                       assert.strictEqual( defer.state(), "rejected", "Deferred is rejected (state)" );
                } ).always( function() {
                        assert.ok( true, "Always callback on reject" );
                } );
@@ -405,21 +406,31 @@ QUnit.test( "[PIPE ONLY] jQuery.Deferred.pipe - deferred (progress)", function(
 
 QUnit.test( "jQuery.Deferred.then - context", function( assert ) {
 
-       assert.expect( 7 );
+       assert.expect( 11 );
 
        var defer, piped, defer2, piped2,
-               context = {},
-               done = jQuery.map( new Array( 4 ), function() { return assert.async(); } );
+               context = { custom: true },
+               done = jQuery.map( new Array( 5 ), function() { return assert.async(); } );
 
        jQuery.Deferred().resolveWith( context, [ 2 ] ).then( function( value ) {
+               assert.strictEqual( this, context, "custom context received by .then handler" );
                return value * 3;
        } ).done( function( value ) {
-               assert.notStrictEqual( this, context, "custom context not propagated through .then" );
+               assert.notStrictEqual( this, context,
+                       "custom context not propagated through .then handler" );
                assert.strictEqual( value, 6, "proper value received" );
                done.pop().call();
        } );
 
+       jQuery.Deferred().resolveWith( context, [ 2 ] ).then().done( function( value ) {
+               assert.strictEqual( this, context,
+                       "custom context propagated through .then without handler" );
+               assert.strictEqual( value, 2, "proper value received" );
+               done.pop().call();
+       } );
+
        jQuery.Deferred().resolve().then( function() {
+               assert.strictEqual( this, window, "default context in .then handler" );
                return jQuery.Deferred().resolveWith( context );
        } ).done( function() {
                assert.strictEqual( this, context,
@@ -435,8 +446,7 @@ QUnit.test( "jQuery.Deferred.then - context", function( assert ) {
        defer.resolve( 2 );
 
        piped.done( function( value ) {
-               assert.strictEqual( this, piped,
-                       "default context gets updated to latest promise in the chain" );
+               assert.strictEqual( this, window, ".then handler does not introduce context" );
                assert.strictEqual( value, 6, "proper value received" );
                done.pop().call();
        } );
@@ -447,8 +457,7 @@ QUnit.test( "jQuery.Deferred.then - context", function( assert ) {
        defer2.resolve( 2 );
 
        piped2.done( function( value ) {
-               assert.strictEqual( this, piped2,
-                       "default context updated to latest promise in the chain (without passing function)" );
+               assert.strictEqual( this, window, ".then without handler does not introduce context" );
                assert.strictEqual( value, 2, "proper value received (without passing function)" );
                done.pop().call();
        } );
@@ -456,21 +465,31 @@ QUnit.test( "jQuery.Deferred.then - context", function( assert ) {
 
 QUnit.test( "[PIPE ONLY] jQuery.Deferred.pipe - context", function( assert ) {
 
-       assert.expect( 7 );
+       assert.expect( 11 );
 
        var defer, piped, defer2, piped2,
-               context = {},
-               done = jQuery.map( new Array( 4 ), function() { return assert.async(); } );
+               context = { custom: true },
+               done = jQuery.map( new Array( 5 ), function() { return assert.async(); } );
 
        jQuery.Deferred().resolveWith( context, [ 2 ] ).pipe( function( value ) {
+               assert.strictEqual( this, context, "custom context received by .pipe handler" );
                return value * 3;
        } ).done( function( value ) {
-               assert.strictEqual( this, context, "[PIPE ONLY] custom context correctly propagated" );
+               assert.strictEqual( this, context,
+                       "[PIPE ONLY] custom context propagated through .pipe handler" );
                assert.strictEqual( value, 6, "proper value received" );
                done.pop().call();
        } );
 
+       jQuery.Deferred().resolveWith( context, [ 2 ] ).pipe().done( function( value ) {
+               assert.strictEqual( this, context,
+                       "[PIPE ONLY] custom context propagated through .pipe without handler" );
+               assert.strictEqual( value, 2, "proper value received" );
+               done.pop().call();
+       } );
+
        jQuery.Deferred().resolve().pipe( function() {
+               assert.strictEqual( this, window, "default context in .pipe handler" );
                return jQuery.Deferred().resolveWith( context );
        } ).done( function() {
                assert.strictEqual( this, context,
@@ -486,8 +505,7 @@ QUnit.test( "[PIPE ONLY] jQuery.Deferred.pipe - context", function( assert ) {
        defer.resolve( 2 );
 
        piped.done( function( value ) {
-               assert.strictEqual( this, piped,
-                       "default context gets updated to latest promise in the chain" );
+               assert.strictEqual( this, window, ".pipe handler does not introduce context" );
                assert.strictEqual( value, 6, "proper value received" );
                done.pop().call();
        } );
@@ -498,8 +516,7 @@ QUnit.test( "[PIPE ONLY] jQuery.Deferred.pipe - context", function( assert ) {
        defer2.resolve( 2 );
 
        piped2.done( function( value ) {
-               assert.strictEqual( this, piped2,
-                       "default context updated to latest promise in the chain (without passing function)" );
+               assert.strictEqual( this, window, ".pipe without handler does not introduce context" );
                assert.strictEqual( value, 2, "proper value received (without passing function)" );
                done.pop().call();
        } );
@@ -825,7 +842,8 @@ QUnit.test( "jQuery.when - joined", function( assert ) {
                        eventuallyRejected: true,
                        rejectedStandardPromise: true
                },
-               counter = 49;
+               counter = 49,
+               expectedContext = (function() { "use strict"; return this; })();
 
        QUnit.stop();
 
@@ -840,15 +858,13 @@ QUnit.test( "jQuery.when - joined", function( assert ) {
                        var shouldResolve = willSucceed[ id1 ] && willSucceed[ id2 ],
                                shouldError = willError[ id1 ] || willError[ id2 ],
                                expected = shouldResolve ? [ 1, 1 ] : [ 0, undefined ],
-                               code = "jQuery.when( " + id1 + ", " + id2 + " )",
-                               context1 = defer1 && jQuery.isFunction( defer1.promise ) ? defer1.promise() : window,
-                               context2 = defer2 && jQuery.isFunction( defer2.promise ) ? defer2.promise() : window;
+                               code = "jQuery.when( " + id1 + ", " + id2 + " )";
 
                        jQuery.when( defer1, defer2 ).done( function( a, b ) {
                                if ( shouldResolve ) {
                                        assert.deepEqual( [ a, b ], expected, code + " => resolve" );
-                                       assert.strictEqual( this[ 0 ], context1, code + " => first context OK" );
-                                       assert.strictEqual( this[ 1 ], context2, code + " => second context OK" );
+                                       assert.strictEqual( this[ 0 ], expectedContext, code + " => context[0] OK" );
+                                       assert.strictEqual( this[ 1 ], expectedContext, code + " => context[1] OK" );
                                } else {
                                        assert.ok( false,  code + " => resolve" );
                                }