这里整理的是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; }
时间: 2024-12-29 01:24:51