Permalink
Browse files

added jQuery.subclass

  • Loading branch information...
deadlyicon committed Oct 10, 2010
1 parent cbf9d87 commit 4024e67d0f352e4a095f93456bc8e6da63e10759
Showing with 98 additions and 5 deletions.
  1. +26 −5 src/core.js
  2. +72 −0 test/unit/core.js
View
@@ -3,7 +3,7 @@ var jQuery = (function() {
// Define a local copy of jQuery
var jQuery = function( selector, context ) {
// The jQuery object is actually just the init constructor 'enhanced'
- return new jQuery.fn.init( selector, context );
+ return new jQuery.fn.init( selector, context, rootjQuery );
},
// Map over jQuery in case of overwrite
@@ -78,7 +78,8 @@ var jQuery = function( selector, context ) {
class2type = {};
jQuery.fn = jQuery.prototype = {
- init: function( selector, context ) {
+ constructor: jQuery,
+ init: function( selector, context, rootjQuery ) {
var match, elem, ret, doc;
// Handle $(""), $(null), or $(undefined)
@@ -112,6 +113,7 @@ jQuery.fn = jQuery.prototype = {
// HANDLE: $(html) -> $(array)
if ( match[1] ) {
+ context = context instanceof jQuery ? context[0] : context;
doc = (context ? context.ownerDocument || context : document);
// If a single string is passed in and it's a single tag
@@ -171,7 +173,7 @@ jQuery.fn = jQuery.prototype = {
// HANDLE: $(expr, context)
// (which is just equivalent to: $(context).find(expr)
} else {
- return jQuery( context ).find( selector );
+ return this.constructor( context ).find( selector );
}
// HANDLE: $(function)
@@ -222,7 +224,7 @@ jQuery.fn = jQuery.prototype = {
// (returning the new matched element set)
pushStack: function( elems, name, selector ) {
// Build a new jQuery matched element set
- var ret = jQuery();
+ var ret = this.constructor();
if ( jQuery.isArray( elems ) ) {
push.apply( ret, elems );
@@ -297,7 +299,7 @@ jQuery.fn = jQuery.prototype = {
},
end: function() {
- return this.prevObject || jQuery(null);
+ return this.prevObject || this.constructor(null);
},
// For internal use only.
@@ -807,6 +809,25 @@ jQuery.extend({
return { browser: match[1] || "", version: match[2] || "0" };
},
+ subclass: function(){
+ function jQuerySubclass( selector, context ) {
+ return new jQuerySubclass.fn.init( selector, context );
+ }
+ jQuerySubclass.superclass = this;
+ jQuerySubclass.fn = jQuerySubclass.prototype = this();
+ jQuerySubclass.fn.constructor = jQuerySubclass;
+ jQuerySubclass.subclass = this.subclass;
+ jQuerySubclass.fn.init = function init( selector, context ) {
+ if (context && context instanceof jQuery && !(context instanceof jQuerySubclass)){
+ context = jQuerySubclass(context);
+ }
+ return jQuery.fn.init.call( this, selector, context, rootjQuerySubclass );
+ };
+ jQuerySubclass.fn.init.prototype = jQuerySubclass.fn;
+ var rootjQuerySubclass = jQuerySubclass(document);
+ return jQuerySubclass;
+ },
+
browser: {}
});
View
@@ -902,3 +902,75 @@ test("jQuery.parseJSON", function(){
ok( true, "Test malformed JSON string." );
}
});
+
+test("jQuery.subclass", function(){
+ expect(378);
+
+ var Subclass = jQuery.subclass(),
+ SubclassSubclass = Subclass.subclass(),
+ jQueryDocument = jQuery(document),
+ selectors, contexts, methods, method, arg, description;
+
+ jQueryDocument.toString = function(){ return 'jQueryDocument'; };
+
+ Subclass.fn.subclassMethod = function(){};
+ SubclassSubclass.fn.subclassSubclassMethod = function(){};
+
+ selectors = [
+ 'body',
+ 'html, body',
+ '<div></div>'
+ ]
+
+ methods = [ // all methods that return a new jQuery instance
+ ['eq', 1],
+ ['add', document],
+ ['end'],
+ ['has'],
+ ['closest', 'div'],
+ ['filter', document],
+ ['find']
+ ]
+
+ contexts = [undefined, document, jQueryDocument];
+
+ jQuery.each(selectors, function(i, selector){
+
+ jQuery.each(methods, function(){
+ method = this[0]
+ arg = this[1]
+
+ jQuery.each(contexts, function(i, context){
+
+ description = '("'+selector+'", '+context+').'+method+'('+(arg||'')+')';
+
+ same(
+ jQuery(selector, context)[method](arg).subclassMethod, undefined,
+ 'jQuery'+description+' doesnt have Subclass methods'
+ );
+ same(
+ jQuery(selector, context)[method](arg).subclassSubclassMethod, undefined,
+ 'jQuery'+description+' doesnt have SubclassSubclass methods'
+ );
+ same(
+ Subclass(selector, context)[method](arg).subclassMethod, Subclass.fn.subclassMethod,
+ 'Subclass'+description+' has Subclass methods'
+ );
+ same(
+ Subclass(selector, context)[method](arg).subclassSubclassMethod, undefined,
+ 'Subclass'+description+' doesnt have SubclassSubclass methods'
+ );
+ same(
+ SubclassSubclass(selector, context)[method](arg).subclassMethod, Subclass.fn.subclassMethod,
+ 'SubclassSubclass'+description+' has Subclass methods'
+ );
+ same(
+ SubclassSubclass(selector, context)[method](arg).subclassSubclassMethod, SubclassSubclass.fn.subclassSubclassMethod,
+ 'SubclassSubclass'+description+' has SubclassSubclass methods'
+ );
+
+ })
+ });
+ });
+
+});

6 comments on commit 4024e67

@addyosmani

This comment has been minimized.

Show comment
Hide comment
@addyosmani

addyosmani Dec 28, 2010

What does jQuery.subclass actually help one achieve? (we talked about this earlier but just asking so we have a public thread available for anyone that wants to read more about it) :)

What does jQuery.subclass actually help one achieve? (we talked about this earlier but just asking so we have a public thread available for anyone that wants to read more about it) :)

@deadlyicon

This comment has been minimized.

Show comment
Hide comment
@deadlyicon

deadlyicon Dec 28, 2010

Owner

It would allow someone to create a personal/private child of the jQuery object and extend it with their own attributes or overwrite existing functions without breaking the main/public jQuery object. Currently the only way to do this is to load an entirely new instance of jQuery.

Owner

deadlyicon replied Dec 28, 2010

It would allow someone to create a personal/private child of the jQuery object and extend it with their own attributes or overwrite existing functions without breaking the main/public jQuery object. Currently the only way to do this is to load an entirely new instance of jQuery.

@SlexAxton

This comment has been minimized.

Show comment
Hide comment
@SlexAxton

SlexAxton Dec 28, 2010

I like this for large applications because it allows you to expose useful sets of functions to yourself internally without exposing everything externally to your users. Code example:

(function(globaljQuery, $) {
  // private plugins
  $.fn.privateSomething = function(){ };
  // load in your copy of X plugin without overriding someone else's older version
  $.fn.cycle = function(){ };

  // Then you can intentionally leak out stuff you want people to have
  globaljQuery.fn.myActualApp = function(){ };

})(jQuery, jQuery.subClass());

It's almost like the module pattern meets the jQuery plugin pattern.

The only thing I might consider in addition to this would maybe be automatic namespacing of events and stuff? I don't know if it'd be possible, but it may be to your advantage to not be able to destroy data of an outer jQuery on accident. That seems to be the last place there are potential conflicts, though using namespaces inside your jQuery subclass as a best practice would also solve the issue and offer more flexibility...

+1 on this.

I like this for large applications because it allows you to expose useful sets of functions to yourself internally without exposing everything externally to your users. Code example:

(function(globaljQuery, $) {
  // private plugins
  $.fn.privateSomething = function(){ };
  // load in your copy of X plugin without overriding someone else's older version
  $.fn.cycle = function(){ };

  // Then you can intentionally leak out stuff you want people to have
  globaljQuery.fn.myActualApp = function(){ };

})(jQuery, jQuery.subClass());

It's almost like the module pattern meets the jQuery plugin pattern.

The only thing I might consider in addition to this would maybe be automatic namespacing of events and stuff? I don't know if it'd be possible, but it may be to your advantage to not be able to destroy data of an outer jQuery on accident. That seems to be the last place there are potential conflicts, though using namespaces inside your jQuery subclass as a best practice would also solve the issue and offer more flexibility...

+1 on this.

@paulirish

This comment has been minimized.

Show comment
Hide comment
@paulirish

paulirish Jan 5, 2011

I wonder if creating new (global) jquery objects gets faster as you dont have to increase the size of the prototype so much... ?

I wonder if creating new (global) jquery objects gets faster as you dont have to increase the size of the prototype so much... ?

@paulirish

This comment has been minimized.

Show comment
Hide comment
@paulirish

paulirish Jan 5, 2011

I wonder if creating new (global) jquery objects gets faster as you dont have to increase the size of the prototype so much... ?

I wonder if creating new (global) jquery objects gets faster as you dont have to increase the size of the prototype so much... ?

@SlexAxton

This comment has been minimized.

Show comment
Hide comment
@SlexAxton

SlexAxton Jan 5, 2011

That would likely be faster, but it wouldn't have the same effect of being able to use all the parent's plugins (or allow you to leak your own back up the chain / or share data, etc.) - if that wasn't a necessity, then i'd say that'd be better. Is that possible already with $.fn.init ? ( I don't think it is )

That would likely be faster, but it wouldn't have the same effect of being able to use all the parent's plugins (or allow you to leak your own back up the chain / or share data, etc.) - if that wasn't a necessity, then i'd say that'd be better. Is that possible already with $.fn.init ? ( I don't think it is )

Please sign in to comment.