zoukankan      html  css  js  c++  java
  • jquery源码解析:proxy,access,swap,isArraylike详解

    jQuery的工具方法,其实就是静态方法,源码里面就是通过extend方法,把这些工具方法添加给jQuery构造函数的。

    jQuery.extend({ 

      ...... 

      guid: 1,    

      //唯一标识符,跟事件有关。举个例子:function show(){alert(this);}, $("#input1").click(show),$("#input2").click(function(){$("#input1").off()}),这里的show方法是事件方法,所以通过off取消掉事件绑定,可以很容易找到事件方法show。但是如果把 $("#input1").click(show)改成 $("#input1").click($.proxy(show,window)),这时show不是事件方法,而是普通方法,那么通过off取消的时候,它是怎么找到这个普通方法show的,其实就是通过guid,因为guid会累加,所以是唯一的,因此可以找到。请看下个方法就知道详情了。

      proxy: function( fn, context ) {    

        //改变方法(函数)执行的this指向。举例:$.proxy(show,document),想给show传参的话,有两种方式:var fn = $.proxy(show,document,1,2);fn(3,4)。最终show执行时就会变成show(1,2,3,4),proxy返回一个函数,调用fn时,就会执行show方法。
        var tmp, args, proxy;

        if ( typeof context === "string" ) {   //这里处理特殊调用情况,比如:$.proxy(obj,"show")(正常写法$.proxy(obj.show,obj)),show方法执行时,this指向的obj,并且show是obj的属性方法.var obj = { show: function(){}}。
          tmp = fn[ context ];
          context = fn;
          fn = tmp;
        }

        if ( !jQuery.isFunction( fn ) ) {
          return undefined;
        }

        args = core_slice.call( arguments, 2 );  //传入的参数,相当于例子的[1,2]
        proxy = function() {
          return fn.apply( context || this, args.concat( core_slice.call( arguments ) ) );  //把[3,4]和[1,2]合并成[1,2,3,4]
        };

        proxy.guid = fn.guid = fn.guid || jQuery.guid++;

        //第一次时,fn.guid(show.guid)是undefined,proxy.guid = fn.guid = 1,show.guid =1, 

        //function() {return fn.apply( context || this, args.concat( core_slice.call( arguments ) ) )}.guid=1,唯一标识,取消绑定时,可以用到。

        return proxy;
      },

      //$().css(),$().attr(),通过参数的不同,实现get/set。参数的个数,以及参数的类型。$("div").css("width"),获得第一个div元素的width,$("div").css("width",100)设置所有的div元素的width。$("div").css({100,height:200}),也是设置所有的div元素,尽管只有一个参数,但是类型不一样。JQuery中有很多这种方法,所以统一用access实现。

      access: function( elems, fn, key, value, chainable, emptyGet, raw ) {

        //elems操作的元素,可能是一个集合。fn是一个回调函数(有区别的在回调函数中处理,比如,css设置样式,attr设置属性)。key和value就是属性名和属性值。chainable为true,设置,为false就获取。
        var i = 0,
          length = elems.length,
          bulk = key == null;

        if ( jQuery.type( key ) === "object" ) {//处理这种类型$("div").css({100,height:200})
          chainable = true;
          for ( i in key ) {
            jQuery.access( elems, fn, i, key[i], true, emptyGet, raw );
          }

        }

        else if ( value !== undefined ) {   //处理这种$("div").css("width",100)
          chainable = true;

          if ( !jQuery.isFunction( value ) ) {
            raw = true;    //字符串(数字)时
          }

          if ( bulk ) {   //如果没有key值
            if ( raw ) {   //如果value是字符串(数字)
              fn.call( elems, value );   //调用回调方法
              fn = null;   //把回调方法赋为空

            }

            else {     //如果是函数,这里面的不用深入理解
              bulk = fn;
              fn = function( elem, key, value ) {
                return bulk.call( jQuery( elem ), value );
              };
            }
          }

          if ( fn ) {    //如果没有key值,并且value是字符串(数字),这里就为null,不会执行
            for ( ; i < length; i++ ) {
              fn( elems[i], key, raw ? value : value.call( elems[i], i, fn( elems[i], key ) ) );
            }
          }
        }

        return chainable ?     //获取时,chainable为false
            elems :     //设置时,chainable为true,直接返回元素,进行后续的链式操作

              bulk ?   
                fn.call( elems ) :  //没有key值时,就回调
                  length ? fn( elems[0], key ) : emptyGet;  //有key值时,判断元素有没有元素,有的话就获取第一个元素的key值(属性名的值),没有元素的话,就返回emptyGet。
      },

      now: Date.now,  //当前时间距离1970年的毫秒数,相当于(new Date()).getTime()

      //以下方法是处理这种情况的:

      //<div id="div1" style="100px;height:100px;background:red;display:none;">ddd</div> 

    $("#div1").get(0).offsetWidth取到的是0,因为它是display:none,不存在DOM树中。$("#div1").width()取到的是100,为啥jQuery可以。因为jQuery会对display:none的元素进行处理,变成<div id="div1" style="100px;height:100px;background:red;display:block;visibility:hidden;position:absolute">ddd</div>,这里就可以通过$("#div1").get(0).offsetWidth取到100了,然后再把新添加的样式去掉。

      swap: function( elem, options, callback, args ) {  //css的转换,内部使用
        var ret, name,
          old = {};

        for ( name in options ) {    

          //保存老样式,插入新样式。这里假设options={100px;height:100px;background:red;display:block;visibility:hidden;position:absolute}

          //elem.style = {100px;height:100px;background:red;display:none;}
          old[ name ] = elem.style[ name ]; 
          elem.style[ name ] = options[ name ];
        }

        ret = callback.apply( elem, args || [] );  

        //通过插入的新样式来获取元素的css值,callback = function(args){ if(args不是[]) return this[args]},args= offsetWidth;

        for ( name in options ) {       //恢复老样式
          elem.style[ name ] = old[ name ];
        }

        return ret;
      }

      ......

    })

    最后讲一下这个方法:

    function isArraylike( obj ) {//判断是否是数组,类数组,带length的json,是的话就返回真
      var length = obj.length,
      type = jQuery.type( obj );

      if ( jQuery.isWindow( obj ) ) {  //担心window对象有length属性
        return false;
      }

      if ( obj.nodeType === 1 && length ) {  

      //元素节点对象,并且有length属性,返回真。document.getElementsByTagName("div")和body.childNodes都不是这种情况。可能用于内部调用,这里如果有谁知道的,可以告诉我。

        return true;
      }

      return type === "array" || type !== "function" &&   //不能是函数,因为函数也可能有length属性
        ( length === 0 ||typeof length === "number" && length > 0 && ( length - 1 ) in obj );

        //typeof length === "number" && length > 0 && ( length - 1 ) in obj )处理{0:"a",1:"b",length:2}这种情况。length === 0处理arguments为空的时候,就是不传入函数任何数据,这时函数中的arguments的length为0,但是是类数组。document.getElementsByTagName("div")和body.childNodes也是类数组。
    }

    加油!

  • 相关阅读:
    1."问吧APP"客户需求调查分析
    “软件工程”课程的学习目标
    范式
    知乎:有哪些让你相见恨晚的 PPT 制作技术或知识?
    前端指南
    在网页中JS函数自动执行常用三种方法
    index的用法
    搜索引擎的正确姿势
    display与visibility
    bootstrap之模态框
  • 原文地址:https://www.cnblogs.com/chaojidan/p/4158786.html
Copyright © 2011-2022 走看看