Ruby's Louvre

每天学习一点点算法

导航

jQuery源码学习笔记五

在正式深入jQuery的核心功能选择器之前,还有一些方法,基本都是数组方法,用于遴选更具体的需求,如获得某个元素的所有祖选元素啦,等等。接着是其缓存机制data。

//@author  司徒正美|なさみ|cheng http://www.cnblogs.com/rubylouvre/  All rights reserved
      //去除两边的空白
      trim: function( text ) {
        return (text || "").replace( /^\s+|\s+$/g, "" );
      },
      //转换成数组,很大众的方法
      makeArray: function( array ) {
        var ret = [];
        if( array != null ){
          var i = array.length;
          // The window, strings (and functions) also have 'length'
          if( i == null || typeof array === "string" || jQuery.isFunction(array) || array.setInterval )
            ret[0] = array;//就只有一元素
          else
            while( i )//处理数组
              ret[--i] = array[i];
        }

        return ret;
      },
      //判断是否在数组中,类似indexOf
      inArray: function( elem, array ) {
        for ( var i = 0, length = array.length; i < length; i++ )
        // Use === because on IE, window == document
          if ( array[ i ] === elem )
            return i;

        return -1;
      },
      //把新元素或第二个数组加入第一个数组中
      //类似数组的concat
      merge: function( first, second ) {
        // We have to loop this way because IE & Opera overwrite the length
        // expando of getElementsByTagName
        var i = 0, elem, pos = first.length;
        // Also, we need to make sure that the correct elements are being returned
        // (IE returns comment nodes in a '*' query)
        if ( !jQuery.support.getAll ) {
          while ( (elem = second[ i++ ]) != null )
            if ( elem.nodeType != 8 )
              first[ pos++ ] = elem;

        } else
          while ( (elem = second[ i++ ]) != null )
            first[ pos++ ] = elem;

        return first;
      },
      //过滤重复元素,用done这个普通对象做过滤器(因为键如果同名将被覆盖掉)
      unique: function( array ) {
        var ret = [], done = {};

        try {

          for ( var i = 0, length = array.length; i < length; i++ ) {
            var id = jQuery.data( array[ i ] );

            if ( !done[ id ] ) {
              done[ id ] = true;
              ret.push( array[ i ] );
            }
          }

        } catch( e ) {
          ret = array;
        }

        return ret;
      },
      //类似数组的filter,这方法起得真不好,通常这都是与正则有关的……
      //$.grep( [0,1,2], function(n,i){
      //  return n > 0;
      //});
      //[1, 2]
      grep: function( elems, callback, inv ) {
        var ret = [];
        // Go through the array, only saving the items
        // that pass the validator function
        //写法很特别,callback之前的!是为了防止回调函数没有返回值
        //javascript默认没有返回值的函数都返回undefined,这样一搞
        //就变成true,原来返回true的变成false,我们需要负负得正,中和一下
        //于是!=出场了,而inv也是未必存在的,用!强制转换成布尔
        for ( var i = 0, length = elems.length; i < length; i++ )
          if ( !inv != !callback( elems[ i ], i ) )
            ret.push( elems[ i ] );
        return ret;
      },
      //就是数组中的map
      map: function( elems, callback ) {
        var ret = [];

        // Go through the array, translating each of the items to their
        // new value (or values).
        for ( var i = 0, length = elems.length; i < length; i++ ) {
          var value = callback( elems[ i ], i );

          if ( value != null )
            ret[ ret.length ] = value;
        }

        return ret.concat.apply( [], ret );
      }
    });

    // jQuery.browser下面的方法已经被废弃了,这些都是为兼容以前的版本与插件用

    var userAgent = navigator.userAgent.toLowerCase();

    // Figure out what browser is being used
    jQuery.browser = {
      version: (userAgent.match( /.+(?:rv|it|ra|ie)[\/: ]([\d.]+)/ ) || [0,'0'])[1],
      safari: /webkit/.test( userAgent ),
      opera: /opera/.test( userAgent ),
      msie: /msie/.test( userAgent ) && !/opera/.test( userAgent ),
      mozilla: /mozilla/.test( userAgent ) && !/(compatible|webkit)/.test( userAgent )
    };
    //把以下方法parent,parents,next……添加到jQuery的原型上去,都是一些过滤方法
    jQuery.each({
      parent: function(elem){return elem.parentNode;},
      parents: function(elem){return jQuery.dir(elem,"parentNode");},
      next: function(elem){return jQuery.nth(elem,2,"nextSibling");},
      prev: function(elem){return jQuery.nth(elem,2,"previousSibling");},
      nextAll: function(elem){return jQuery.dir(elem,"nextSibling");},
      prevAll: function(elem){return jQuery.dir(elem,"previousSibling");},
      siblings: function(elem){return jQuery.sibling(elem.parentNode.firstChild,elem);},
      children: function(elem){return jQuery.sibling(elem.firstChild);},
      contents: function(elem){return jQuery.nodeName(elem,"iframe")?elem.contentDocument||elem.contentWindow.document:jQuery.makeArray(elem.childNodes);}
    }, function(name, fn){
      jQuery.fn[ name ] = function( selector ) {//方法体
        var ret = jQuery.map( this, fn );

        if ( selector && typeof selector == "string" )
          ret = jQuery.multiFilter( selector, ret );

        return this.pushStack( jQuery.unique( ret ), name, selector );
      };
    });
    //把以下方法appendTo,prependTo,insertBefore……添加到jQuery的原型上去,
    //利用已有的append,prepend……方法构建
    jQuery.each({
      appendTo: "append",
      prependTo: "prepend",
      insertBefore: "before",
      insertAfter: "after",
      replaceAll: "replaceWith"
    }, function(name, original){
      jQuery.fn[ name ] = function( selector ) {
        var ret = [], insert = jQuery( selector );

        for ( var i = 0, l = insert.length; i < l; i++ ) {
          var elems = (i > 0 ? this.clone(true) : this).get();
          jQuery.fn[ original ].apply( jQuery(insert[i]), elems );
          ret = ret.concat( elems );
        }

        return this.pushStack( ret, name, selector );
      };
    });

     //一些重要常用的静态方法
      jQuery.each({
        removeAttr: function( name ) {
          jQuery.attr( this, name, "" );
          if (this.nodeType == 1)
            this.removeAttribute( name );
        },

        addClass: function( classNames ) {
          jQuery.className.add( this, classNames );
        },

        removeClass: function( classNames ) {
          jQuery.className.remove( this, classNames );
        },

        toggleClass: function( classNames, state ) {
          if( typeof state !== "boolean" )
            state = !jQuery.className.has( this, classNames );
          jQuery.className[ state ? "add" : "remove" ]( this, classNames );
        },

        remove: function( selector ) {
          if ( !selector || jQuery.filter( selector, [ this ] ).length ) {
            // Prevent memory leaks
            jQuery( "*", this ).add([this]).each(function(){
              jQuery.event.remove(this);//★★★★★
              jQuery.removeData(this);
            });
            if (this.parentNode)
              this.parentNode.removeChild( this );
          }
        },

        empty: function() {
          // Remove element nodes and prevent memory leaks
          jQuery(this).children().remove();

          // Remove any remaining nodes
          while ( this.firstChild )
            this.removeChild( this.firstChild );
        }
      }, function(name, fn){
        jQuery.fn[ name ] = function(){
          return this.each( fn, arguments );
        };
      });
      //将带单位的数值去掉单位
      // Helper function used by the dimensions and offset modules
      function num(elem, prop) {
        return elem[0] && parseInt( jQuery.curCSS(elem[0], prop, true), 10 ) || 0;
      }

接着下来看jQuery的缓存机制,jQuery的性能很大部分依仗于它。

//@author  司徒正美|RestlessDream|なさみ|cheng http://www.cnblogs.com/rubylouvre/  All rights reserved
      var expando = "jQuery" + now(), uuid = 0, windowData = {};

      jQuery.extend({
        cache: {},

        data: function( elem, name, data ) {
          //坚决不染指window
          elem = elem == window ?
            windowData :
            elem;
          //在elem上设置一个变量
          var id = elem[ expando ];

          // Compute a unique ID for the element
          if ( !id )
          //  同时为id,elem[expando]赋值,值为单一数字
            id = elem[ expando ] = ++uuid;

          // Only generate the data cache if we're
          // trying to access or manipulate it
          if ( name && !jQuery.cache[ id ] )
          //在jQuery.cache上开辟一个对象,专门用于储存与那个elem有关的东西
            jQuery.cache[ id ] = {};

          // Prevent overriding the named cache with undefined values
          if ( data !== undefined )//data必须定义
            jQuery.cache[ id ][ name ] = data;

          // Return the named cache data, or the ID for the element
          //根据第二个参数是否存在决定返回的是缓存数据还是element的特别ID
          return name ?
            jQuery.cache[ id ][ name ] :
            id;
        },
        //移除缓存数据
        removeData: function( elem, name ) {
          elem = elem == window ?
            windowData :
            elem;

          var id = elem[ expando ];

          // If we want to remove a specific section of the element's data
          if ( name ) {
            if ( jQuery.cache[ id ] ) {
              // Remove the section of cache data
              delete jQuery.cache[ id ][ name ];

              // If we've removed all the data, remove the element's cache
              name = "";

              for ( name in jQuery.cache[ id ] )
                break;

              if ( !name )
                jQuery.removeData( elem );
            }

            // Otherwise, we want to remove all of the element's data
          } else {
            // Clean up the element expando
            try {
              //IE不能直接用delete去移除,要用removeAttribute
              delete elem[ expando ];
            } catch(e){
              // IE has trouble directly removing the expando
              // but it's ok with using removeAttribute
              if ( elem.removeAttribute )
                elem.removeAttribute( expando );
            }

            // Completely remove the data cache
            //用缓存体中把其索引值也移掉
            delete jQuery.cache[ id ];
          }
        },
        //缓存元素的类组数属性
        //可读写
        queue: function( elem, type, data ) {
          if ( elem ){

            type = (type || "fx") + "queue";

            var q = jQuery.data( elem, type );

            if ( !q || jQuery.isArray(data) )
            //q是数组
              q = jQuery.data( elem, type, jQuery.makeArray(data) );
            else if( data )
              q.push( data );

          }
          return q;
        },
        //对元素的类数组缓存进行dequeue(也就是shift)
        dequeue: function( elem, type ){
          var queue = jQuery.queue( elem, type ),
          fn = queue.shift();

          if( !type || type === "fx" )
            fn = queue[0];

          if( fn !== undefined )
            fn.call(elem);
        }
      });
      //让jQuery对象也能获得这种缓存能力
      //都是用上面静态方法实现,最终的缓存体还是jQuery.cache
      jQuery.fn.extend({
        data: function( key, value ){
          var parts = key.split(".");
          parts[1] = parts[1] ? "." + parts[1] : "";

          if ( value === undefined ) {
            var data = this.triggerHandler("getData" + parts[1] + "!", [parts[0]]);

            if ( data === undefined && this.length )
              data = jQuery.data( this[0], key );

            return data === undefined && parts[1] ?
              this.data( parts[0] ) :
              data;
          } else
            return this.trigger("setData" + parts[1] + "!", [parts[0], value]).each(function(){
              jQuery.data( this, key, value );
            });
        },

        removeData: function( key ){
          return this.each(function(){
            jQuery.removeData( this, key );
          });
        },
        queue: function(type, data){
          if ( typeof type !== "string" ) {
            data = type;
            type = "fx";
          }

          if ( data === undefined )
            return jQuery.queue( this[0], type );

          return this.each(function(){
            var queue = jQuery.queue( this, type, data );

            if( type == "fx" && queue.length == 1 )
              queue[0].call(this);
          });
        },
        dequeue: function(type){
          return this.each(function(){
            jQuery.dequeue( this, type );
          });
        }
      });

posted on 2009-11-21 20:01  司徒正美  阅读(4154)  评论(7编辑  收藏  举报