WEB开发网
开发学院网页设计JavaScript jquery构造器的实现 阅读

jquery构造器的实现

 2011-12-02 22:08:10 来源:WEB开发网   
核心提示:jQuery的$符号非常神奇,它可以接受一个字符,jquery构造器的实现,也可以接受一个文档对象或window对象,亦可以传个函数进行变为domReady加载器,我们还需要一点选择器的知识与了解buildFragment方法的运作,因为这两个实在太常用了,显然,能做到这一步

jQuery的$符号非常神奇,它可以接受一个字符,也可以接受一个文档对象或window对象,亦可以传个函数进行变为domReady加载器。显然,能做到这一步,其实现是相当的复杂,这个实现就是它的init方法,jQuery的真实构造器。它功能也随着版本的升级而升级,越来越长。

2009-01-13发布的1.3版

init: function( selector, context ) { 

   // Make sure that a selection was PRovided 

   selector = selector || document; 

 

   // 处理节点参数,直接添加属性到新实例上 

   if ( selector.nodeType ) { 

   this[0] = selector; 

   this.length = 1; 

   this.context = selector; 

   return this; 

   } 

   // 处理字符串参数 

   if ( typeof selector === "string" ) { 

   // 判定是否为HTML片断还是ID 

   var match = quickExpr.exec( selector ); 

  

   if ( match && (match[1] || !context) ) { 

 

     // 如果是HTML片断,转换一个由节点构造的数组 

     if ( match[1] ) 

       selector = jQuery.clean( [ match[1] ], context ); 

 

     // 如果是ID,则查找此元素,如果找到放进空数组中 

     else { 

       var elem = document.getElementById( match[3] ); 

 

       // Make sure an element was located 

       if ( elem ){ 

         // 处理 IE and Opera 混淆ID与NAME的bug 

         if ( elem.id != match[3] ) 

           return jQuery().find( selector ); 

         var ret = jQuery( elem ); 

         ret.context = document; 

         ret.selector = selector; 

         return ret; 

       } 

       selector = []; 

     } 

   } else

   //使用Sizzle处理其他CSS表达式,生成实例并返回 

     return jQuery( context ).find( selector ); 

   // 处理函数参数,直接domReady 

   } else if ( jQuery.isFunction( selector ) ) 

   return jQuery( document ).ready( selector ); 

 

   //处理jQuery对象参数,简单地将其两个属性赋给新实例 

   if ( selector.selector && selector.context ) { 

   this.selector = selector.selector; 

   this.context = selector.context; 

   } 

   //将上面得到节点数组,用setArray方法把它们变成实例的元素 

   return this.setArray(jQuery.makeArray(selector)); 

},

2009-02-19发布的1.32版

init: function( selector, context ) { 

   // Make sure that a selection was provided 

   selector = selector || document; 

   // 处理节点参数,直接添加属性到新实例上 

   if ( selector.nodeType ) { 

   this[0] = selector; 

   this.length = 1; 

   this.context = selector; 

   return this; 

   } 

//处理字符串参数 

   if ( typeof selector === "string" ) { 

   //判定是否为HTML片断还是ID 

   var match = quickExpr.exec( selector ); 

   if ( match && (match[1] || !context) ) { 

    // 如果是HTML片断,转换一个由节点构造的数组 

     if ( match[1] ) 

       selector = jQuery.clean( [ match[1] ], context ); 

     else { 

       var elem = document.getElementById( match[3] ); 

 

       // 如果是ID,则查找此元素,如果找到放进空数组中 

       if ( elem && elem.id != match[3] ) 

         return jQuery().find( selector ); 

 

       //这里对1.3版做了些优化,更简洁 

       var ret = jQuery( elem || [] ); 

       ret.context = document; 

       ret.selector = selector; 

       return ret; 

     } 

   } else

     //使用Sizzle处理其他CSS表达式,生成实例并返回 

     return jQuery( context ).find( selector ); 

 

   // 处理函数参数,进行domReady操作 

   } else if ( jQuery.isFunction( selector ) ) 

   return jQuery( document ).ready( selector ); 

 

//处理jQuery对象参数,简单地将其两个属性赋给新实例 

   if ( selector.selector && selector.context ) { 

   this.selector = selector.selector; 

   this.context = selector.context; 

   } 

//这里对1.3版做了些扩展,允许传珍上元素集合(HTMLCollection)与节点集合(NodeList), 

//元素数组可能是我们用字符串转换过来的,也可以是用户直接传进来的 

   return this.setArray(jQuery.isArray( selector ) ? selector : jQuery.makeArray(selector)); 

},

2010-01-13发布的1.4版

init: function( selector, context ) { 

   var match, elem, ret, doc; 

 

   //处理空白字符串,null,undefined参数(新增),返回一个非常纯净的实例 

   if ( !selector ) { 

   return this; 

   } 

 

   // 处理节点参数,直接添加属性到新实例上 

   if ( selector.nodeType ) { 

   this.context = this[0] = selector;//写法上优化 

   this.length = 1; 

   return this; 

   } 

 

   //处理字符串参数 

   if ( typeof selector === "string" ) { 

   //  判定是否为HTML片断还是ID 

   match = quickExpr.exec( selector ); 

   if ( match && (match[1] || !context) ) { 

 

     //如果是HTML片断 

     if ( match[1] ) { 

       //取得文档对象 

       doc = (context ? context.ownerDocument || context : document); 

 

       // 如果是单个标签,直接使用 document.createElement创建此节点并放入数组中 

       ret = rsingleTag.exec( selector ); 

 

       if ( ret ) { 

         //如果后面跟着一个纯净的JS对象,则为此节点添加相应的属性或样式 

         if ( jQuery.isPlainObject( context ) ) { 

           selector = [ document.createElement( ret[1] ) ]; 

           jQuery.fn.attr.call( selector, context, true ); 

         } else { 

           selector = [ doc.createElement( ret[1] ) ]; 

         } 

 

       } else { 

         //改由buildFragment来生成节点集合(NodeList) 

         ret = buildFragment( [ match[1] ], [ doc ] ); 

         selector = (ret.cacheable ? ret.fragment.cloneNode(true) : ret.fragment).childNodes; 

       } 

     } else { 

       // 如果是ID,则查找此元素,如果找到放进空数组中 

       elem = document.getElementById( match[2] ); 

 

       if ( elem ) { 

         // 处理 IE and Opera 混淆ID与NAME的bug 

         if ( elem.id !== match[2] ) { 

           return rootjQuery.find( selector ); 

         } 

         //这里也做了一些优化,原来是很傻地再生成一个jQuery实例 

         this.length = 1; 

         this[0] = elem; 

       } 

       this.context = document; 

       this.selector = selector; 

       return this; 

     } 

 

     // 如果字符是很简单的标签选择器,那基本没有必要走Sizzle路线,直接getElementsByTagName,很好的优化 

   } else if ( !context && /^\w+$/.test( selector ) ) { 

     this.selector = selector; 

     this.context = document; 

     selector = document.getElementsByTagName( selector ); 

 

     // 如果第二个参数不存在或者是jQuery对象,那么用它或rootjQuery调用find查找目标节点(走Sizzle路线) 

   } else if ( !context || context.jquery ) { 

     return (context || rootjQuery).find( selector ); 

 

     // HANDLE: $(expr, context) 

     // (which is just equivalent to: $(context).find(expr) 

   } else { 

     //如果第二个参数已指定为某元素节点,转为jQuery对象,走Sizzle路线 

     return jQuery( context ).find( selector ); 

   } 

 

   // 处理函数参数,直接domReady 

 

   } else if ( jQuery.isFunction( selector ) ) { 

   return rootjQuery.ready( selector ); 

   } 

   //处理jQuery对象参数,简单地将其两个属性赋给新实例 

   if (selector.selector !== undefined) { 

   this.selector = selector.selector; 

   this.context = selector.context; 

   } 

//这里又做了些许修改,缘于makeArray可以接受第二个参数(可以是数组或类数组,这时相当合并操作) 

   return jQuery.isArray( selector ) ? 

   this.setArray( selector ) ://内部用push方法,迅速将一个普通对象变成类数组对象 

   jQuery.makeArray( selector, this ); 

},

接着是广受欢迎的2010-02-13发布的1.42版

init: function( selector, context ) { 

   var match, elem, ret, doc; 

 

   // 处理空白字符串,null,undefined参数 

   if ( !selector ) { 

   return this; 

   } 

   // 处理节点参数 

   if ( selector.nodeType ) { 

   this.context = this[0] = selector; 

   this.length = 1; 

   return this; 

   }  

   // 处理body参数(新增) 

   if ( selector === "body" && !context ) { 

   this.context = document; 

   this[0] = document.body; 

   this.selector = "body"; 

   this.length = 1; 

   return this; 

   } 

 

   // 处理字符串参数,分七种情形: 

   //①单个标签,带对象属性包      --->  jQuery.merge 

   //②单个标签,不带对象属性包     --->  attr + jQuery.merge 

   //③复杂的HTML片断         --->  buildFragment + jQuery.merge 

   //④ID选择器,与找到的元素的ID不同  --->  getElementById + Sizzle + pushStack 

   //⑤ID选择器,与找到的元素的ID相同  --->  getElementById + 简单属性添加 

   //⑥标签选择器           --->  getElementsByTagName + jQuery.merge 

   //⑦其他CSS表达式          --->  Sizzle + pushStack 

   if ( typeof selector === "string" ) { 

   match = quickExpr.exec( selector ); 

   if ( match && (match[1] || !context) ) { 

     if ( match[1] ) { 

       doc = (context ? context.ownerDocument || context : document); 

       ret = rsingleTag.exec( selector ); 

       if ( ret ) { 

         if ( jQuery.isPlainObject( context ) ) { 

           selector = [ document.createElement( ret[1] ) ]; 

           jQuery.fn.attr.call( selector, context, true ); 

 

         } else { 

           selector = [ doc.createElement( ret[1] ) ]; 

         } 

       } else { 

         ret = buildFragment( [ match[1] ], [ doc ] ); 

         selector = (ret.cacheable ? ret.fragment.cloneNode(true) : ret.fragment).childNodes; 

       } 

         

       return jQuery.merge( this, selector ); 

     } else { 

       elem = document.getElementById( match[2] ); 

 

       if ( elem ) { 

         if ( elem.id !== match[2] ) { 

           return rootjQuery.find( selector ); 

         } 

         this.length = 1; 

         this[0] = elem; 

       } 

 

       this.context = document; 

       this.selector = selector; 

       return this; 

     } 

   } else if ( !context && /^\w+$/.test( selector ) ) { 

     this.selector = selector; 

     this.context = document; 

     selector = document.getElementsByTagName( selector ); 

     return jQuery.merge( this, selector ); 

     

   } else if ( !context || context.jquery ) { 

     return (context || rootjQuery).find( selector ); 

   } else { 

     return jQuery( context ).find( selector ); 

   } 

   // 处理函数参数,直接domReady 

   } else if ( jQuery.isFunction( selector ) ) { 

   return rootjQuery.ready( selector ); 

   } 

   //处理jQuery对象参数 

   if (selector.selector !== undefined) { 

   this.selector = selector.selector; 

   this.context = selector.context; 

   } 

   //无论是数组还是类数组(如NodeList),统统使用jQuery.makeArray来为实例添加新的元素 

   return jQuery.makeArray( selector, this ); 

},

另附上makeArray方法与merge方法,merge方法好神奇啊,

makeArray: function( array, results ) { 

   var ret = results || []; 

 

   if ( array != null ) { 

   // The window, strings (and functions) also have 'length' 

   // The extra typeof function check is to prevent crashes 

   // in Safari 2 (See: #3039) 

   if ( array.length == null || typeof array === "string" || jQuery.isFunction(array) || (typeof array !== "function" && array.setInterval) ) { 

     push.call( ret, array ); 

   } else { 

     jQuery.merge( ret, array ); 

   } 

   } 

 

   return ret; 

}, 

merge: function( first, second ) { 

   var i = first.length, j = 0; 

 

   if ( typeof second.length === "number" ) { 

   for ( var l = second.length; j < l; j++ ) { 

     first[ i++ ] = second[ j ]; 

   } 

 

   } else { 

   while ( second[j] !== undefined ) { 

     first[ i++ ] = second[ j++ ]; 

   } 

   } 

 

   first.length = i; 

 

   return first; 

},

2011-01-23发布的1.5版,其init方法与1.42的变化不大:只有两处做了改动:

//1.42 

-  ret = buildFragment( [ match[1] ], [ doc ] ); 

-  selector = (ret.cacheable ? ret.fragment.cloneNode(true) : ret.fragment).childNodes; 

//1.5 

+ ret = jQuery.buildFragment( [ match[1] ], [ doc ] ); 

+ selector = (ret.cacheable ? jQuery.clone(ret.fragment) : ret.fragment).childNodes; 

 

//1.42 

- return jQuery( context ).find( selector ); 

//1.5 

+ return this.constructor( context ).find( selector );//目的就是为了不再生成新实例

2011-05-02发布的jquery1.6,变化不大,只是对HTML片断进行了更严密的判定:

// Are we dealing with HTML string or an ID? 

  if ( selector.charAt(0) === "<" && selector.charAt( selector.length - 1 ) === ">" && selector.length >= 3 ) { 

  // Assume that strings that start and end with <> are HTML and skip the regex check 

   match = [ null, selector, null ]; 

  } else { 

   match = quickExpr.exec( selector ); 

  }

总体来说,jQuery的构造器已经做得非常之完美,基本上达到“改无可改”的地步了。但是要保证其高效运作,我们还需要一点选择器的知识与了解buildFragment方法的运作,因为这两个实在太常用了,但也是最耗性能的。

Tags:jquery 构造器 实现

编辑录入:爽爽 [复制链接] [打 印]
赞助商链接