jQuery晦涩的底层工具方法们

这里整理的是jQuery源码中一些比较晦涩难懂的、内部的、最底层的工具方法,它们多为jQuery的上层api方法服务,目前包括:

  • jQuery.access

jQuery.access: function( elems, fn, key, value, chainable, emptyGet, pass )

在jQuery的众多api方法中,许多方法都有一个非常重要和常见的特征:重载,简单来讲即参数的不同决定了方法的功能不同

例如我们最常使用的几个:jQuery.fn.val()、jQuery.fn.html()、jQuery.fn.attr()、jQuery.fn.text()、jQuery.fn.css()等等

从这些方法的身上,我们其实可以总结出它们之间一些共同具有的特征:

  • 所有的操作都是在元素节点上
  • 进行的操作都是get或set
  • 都有自己的处理函数
  • 都可以接受函数作为参数干预最终的结果
  • 具备链式调用的能力

源码解析:

// Multifunctional method to get and set values of a collection
// The value/s can optionally be executed if it‘s a function
/** 参数说明
  * elems [object] 元素
  * fn [function] 原始处理函数
  * key [string|object] 属性名称/键值对列表
  * value [type|function] 进行赋值的值/干预函数
  * chainable [boolean] 是否可以链式调用
  * emptyGet [type] 指定的空值,类型不定
  * pass [boolean] 属性与jQuery方法同名时,是否调用jQuery的方法,在attr方法那里会用到      
*/
access: function( elems, fn, key, value, chainable, emptyGet, pass ) {
    var exec,
        bulk = key == null,
        i = 0,
        length = elems.length;

    // Sets many values
    // 如果key是一个键值对对象,那么一定是在赋值,分解key然后递归调用
    if ( key && typeof key === "object" ) {
        for ( i in key ) {
            // 可以看出,赋值操作都是可以继续链式调用,因为chainable直接被赋值为了1
            jQuery.access( elems, fn, i, key[i], 1, emptyGet, value );
        }
        
        // 这里仍然需要手动设置chainable的值为1
        // 因为这里并没有return 
        chainable = 1;

    // Sets one value
    } else if ( value !== undefined ) {
        // Optionally, function values get executed if exec is true
        // 如果没传pass并且传入了干预函数
        exec = pass === undefined && jQuery.isFunction( value );

        // bulk = key == null
        // 即没有传入key,但传了value进来
        if ( bulk ) {
            // Bulk operations only iterate when executing function values
            // value是个函数的情况
            if ( exec ) {
                // 原始处理函数用exec保存起来
                exec = fn;
                // 把原本保存原始处理函数的变量fn重新用约定好的结构进行封装,以便后面统一进行调用
                // 这里的key参数纯粹是为了在后面的调用处统一参数
                // 注意这里的fn函数最后返回了原始处理函数的处理结果,为什么这样做呢?后面解释
                fn = function( elem, key, value ) {
                    return exec.call( jQuery( elem ), value );
                };

            // Otherwise they run against the entire set
            } else {
                // 这是最简单的情况,直接调用原始处理函数进行赋值
                // 这种情况下的赋值操作到这里就已经完成了
                // 所以在这里把fn置为了null,因为后面的if已经没有必要执行了
                fn.call( elems, value );
                fn = null;
            }
        }

        // 剔除上面最简单那种情况,无论传不传key的值,这里的if都会执行
        if ( fn ) {
            for (; i < length; i++ ) {
                // 这里的fn的值包含两种情况,一个是原始处理函数,一个是上面已经封装过的fn
                // 这里最难理解的应该就是fn( elems[i], key )这句话了
                // 首先无论是哪种情况,fn( elems[i], key )这句话一定是一个取值的操作
                // 那么如果fn是原始处理函数,并且传入了key的值,取到的其实就是这个元素的原始属性值,把它作为第二个参数传入了干预函数( $("div").attr( "id", function( index, attr ){} ) )
                // 如果fn是上面封装的函数,表示没有传入key值,则fn中的key和value都是undefined,但仍然仅凭元素就可以取得这个元素的原始属性值,哪种情况呢?如$("div").val("something")、$("div").text("something")
                // 所以,fn( elems[i], key )这句话的作用也就清晰了,就是取得元素的原始属性值
                // 搞懂了上面,这行代码也就不难理解了,这里设计的确实是巧啊
                fn( elems[i], key, exec ? value.call( elems[i], i, fn( elems[i], key ) ) : value, pass );
            }
        }

        // 同样,仍然是可以链式调用
        chainable = 1;
    }

    // 如果可以链式调用,返回元素集即可,函数到此结束
    // 如果不是,则证明是在取值
    // 如果没传key,直接返回fn.call( elems ),如$("div").val()
    // 如果传了key,则返回第一个元素的key对应的属性值或返回指定的空值emptyGet
    return chainable ?
        elems :

        // Gets
        bulk ?
            fn.call( elems ) :
            length ? fn( elems[0], key ) : emptyGet;
}

 

  

郑重声明:本站内容如果来自互联网及其他传播媒体,其版权均属原媒体及文章作者所有。转载目的在于传递更多信息及用于网络分享,并不代表本站赞同其观点和对其真实性负责,也不构成任何其他建议。