Eliminate use of isPlainObject and deprecate the method
See original GitHub issueDescription
The isPlainObject
function is complicated and unnecessary (certainly internally). I count five cases where it is used in the core. Here is one example:
jQuery.each( [ "get", "post" ], function( i, method ) {
jQuery[ method ] = function( url, data, callback, type ) {
// Shift arguments if data argument was omitted
if ( jQuery.isFunction( data ) ) {
type = type || callback;
callback = data;
data = undefined;
}
// The url can be an options object (which then must have .url)
return jQuery.ajax( jQuery.extend( {
url: url,
type: method,
dataType: type,
data: data,
success: callback
}, jQuery.isPlainObject( url ) && url ) );
};
} );
This sort of faux “overloading” is generally ill-advised in ECMAScript. The above code should make that clear as it is relatively inefficient and hard to follow. We won’t clean it up entirely here as can’t break compatibility (and one thing at a time).
What do we have in this case? A string or an Object
object. If it is an object, then it is “mixed in” to the object created to house the various options. Obviously this is a strange interface as we could pass method
, callback
and type
twice (once as the named arguments and again in the url
object).
How about this instead?
jQuery.each( [ "get", "post" ], function( i, method ) {
jQuery[ method ] = function( url, data, callback, type ) {
// Shift arguments if data argument was omitted
if ( jQuery.isFunction( data ) ) {
type = type || callback;
callback = data;
data = undefined;
}
var options = {
type: method,
dataType: type,
data: data,
success: callback
};
// If url is a string...
if (typeof url == 'string') {
options.url = url;
} else {
// If not a string, the only other *allowed* possibility is an Object object
// Calls with anything *other* than a string or an Object object for url
// will have undefined behavior.
// The object must have a "url" property, otherwise behavior is also undefined
jQuery.extend(options, url);
}
return jQuery.ajax( options );
};
} );
And let’s get rid of that isFunction
call as well as that method should get the same treatment in another issue. Just foreshadowing here:
jQuery.each( [ "get", "post" ], function( i, method ) {
jQuery[ method ] = function( url, data, callback, type ) {
// Shift arguments if data argument was omitted
if ( typeof data == 'function' ) {
type = type || callback;
callback = data;
data = undefined;
}
var options = {
type: method,
dataType: type,
data: data,
success: callback
};
// If url is a string...
if (typeof url == 'string') {
options.url = url;
} else {
// If not a string, the only other *allowed* possibility is an Object object
// Calls with anything *other* than a string or an Object object for url
// will have undefined behavior.
// The object must have a "url" property, otherwise the behavior is also undefined
jQuery.extend(options, url);
}
return jQuery.ajax( options );
};
} );
Better, right? Faster and much easier to follow. There’s enough going on in that function to start without adding calls to other odd functions.
It’s important to understand why isFunction
isn’t needed here (or anywhere most likely). It’s a callback, which must be a Function
object. Not any old callable object (e.g. host objects), but an object constructed by Function
. All such Function
objects have the same typeof
result in all browsers: 'function'
.
That’s one down and four to go. Not going to bother with the rest until there is some indication of agreement that this strategy is sound. It will be much the same deal for the rest of the calls to this method and then it can be deprecated (shouldn’t encourage developers to use such functions either). In the bigger picture, there is a lot more in the core that can be made easier to follow through similar restructuring.
Link to test case
No test case. Behavior is to remain the same, so existing test cases are appropriate.
Issue Analytics
- State:
- Created 7 years ago
- Comments:17 (6 by maintainers)
Thanks, but we’re not yet done here.
Here’s the thing “Brain”: that was a sample of 5 required changes before
isPlainObject
is deprecated and eventually deleted, which will easily make up for any additions incurred.The idea of checking every commit to see whether it added a few bytes to the file size is just wrong. It’s premature… something. Call it premature counting of bytes. It’s like checking the gas level to the ounce every time you hit a stop light.
Leave it until a trend develops, which will most likely be a downward one for my fork (there’s plenty of fat to trim in this project). But what if over time it increased it 1K? Or 2K? There are other factors to weigh against size increases (e.g. code clarity, performance).
Anyway, on to round two…
Re-did
extend
and without splitting it in two. Turns out that wouldn’t have made much difference.https://github.com/jquery/jquery/issues/3444
As expected, didn’t need near the amount of code in
isPlainObject
forextend
. The needed bits (slightly rewritten for clarity and speed) are now “private” toextend
. ThejQuery.isPlainObject
method is now deprecated.The
deprecated
function, which will be used in other soon to follow methods:Though there used to be a similar function, but could only find code like this in
Deferred
exception handling. Should likely restructure so that all of the code can share a commonwarn
function.And that’s that. As mentioned in the new
extend
rehab issue, the lessons learned during the investigation and adjustment of the affected code is indicative of the sort of work that is required throughout this project (if it is to get off the treadmill of nagging bug fixes). See this as the only way to regain and maintain any semblance of relevancy.There are other problems unrelated to this theme (e.g. multi-browser builds), but it would be pointless to get into them until these basic issues are addressed.
Strongly advise reopening this ticket. Even if we buy into none of the above, there are clear mistakes and inconsistencies that have been uncovered in previous comments. Read it carefully from the top and they should be apparent.