首页 > 代码库 > jQuery剥皮三- data、proxy、event

jQuery剥皮三- data、proxy、event

jquery1.4  jquery1.4下载

这里使用了 jQuery1.4,为什么使用 1.4 因为 1.4 很多特性没有添加分析起来相对容易。


这个 data 的实现是扩展在 jQuery 静态函数里面的,我们平常这样( $(‘#data‘).data(‘tudou‘, ‘abc‘) )调用的是 jQuery 原型上的 data ,原型上面的 data 再调用下面 jQuery 静态的 data 方法实现。

jQuery.extend({
	cache: {},
	
	expando:expando,

	// The following elements throw uncatchable exceptions if you
	// attempt to add expando properties to them.
	noData: {
		"embed": true,
		"object": true,
		"applet": true
	},

	data: function( elem, name, data ) {
        // 不是正确的元素过滤掉
		if ( elem.nodeName && jQuery.noData[elem.nodeName.toLowerCase()] ) {
			return;
		}

		elem = elem == window ?
			windowData :
			elem;

		var id = elem[ expando ], cache = jQuery.cache, thisCache;

		// Handle the case where there‘s no name immediately
        // 没有 data 名 且 也没有设置过 data 返回
		if ( !name && !id ) {
			return null;
		}

		// Compute a unique ID for the element
		if ( !id ) { 
			id = ++uuid;
		}

		// Avoid generating a new cache unless none exists and we
		// want to manipulate it.
		if ( typeof name === "object" ) {
			elem[ expando ] = id;
            // 如果传递过来的是对象,直接扩展到 cache 对象中
			thisCache = cache[ id ] = jQuery.extend(true, {}, name);
		} else if ( cache[ id ] ) {
            // 如果有存储过,则拿到以前的
			thisCache = cache[ id ];
		} else if ( typeof data === "undefined" ) {
			thisCache = emptyObject;
		} else {
		        // 第一次存储,要新建一个空对象
			thisCache = cache[ id ] = {};
		}

		// Prevent overriding the named cache with undefined values
        // 把数据写入 cache 存储 根据uuid
		if ( data !== undefined ) {
			elem[ expando ] = id;
			thisCache[ name ] = data;
		}
        // 返回附加的数据
		return typeof name === "string" ? thisCache[ name ] : thisCache;
	},

	removeData: function( elem, name ) {
		if ( elem.nodeName && jQuery.noData[elem.nodeName.toLowerCase()] ) {
			return;
		}

		elem = elem == window ?
			windowData :
			elem;

		var id = elem[ expando ], cache = jQuery.cache, thisCache = cache[ id ];

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

				// If we‘ve removed all the data, remove the element‘s cache
				if ( jQuery.isEmptyObject(thisCache) ) {
					jQuery.removeData( elem );
				}
			}

		// Otherwise, we want to remove all of the element‘s data
		} else {
			// Clean up the element expando
			try {
				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 cache[ id ];
		}
	}
});


这个 data 的实现是在 jQuery 函数上创建了一个静态变量 cache 对象,key 就是 data 的 name, value 就是 data 的 value。如下图:

现在来看一张图,来观察 data 实现的方法

实现方法就是 jQuery 在对元素操作的时候会赋值一个 "jQuery" + now() 时间戳的属性,其值是一个 uuid 唯一的数字,每次操作会加 1 ,然后在 jQuery 的 cache 的对象静态变量中根据这个 uuid 赋值一个 key 就像这样 {5:{}}。并且把你要加入的 data 放去这个 key 为 5 的对象中 {5:{tudou:"abc"}}。他们唯一关联的就是这个 uuid 。

看看 jQuery 的原型实现

jQuery.fn.extend({
	data: function( key, value ) {
		if ( typeof key === "undefined" && this.length ) {
			return jQuery.data( this[0] );
		} else if ( typeof key === "object" ) {
            // 对象直接进行 data 存储
			return this.each(function() {
				jQuery.data( this, key );
			});
		}
		var parts = key.split(".");
		parts[1] = parts[1] ? "." + parts[1] : "";
        // 如果只传 name 就是获取值
		if ( value === undefined ) {
            // 设置会触发 getData 自定义方法 他们都可以通过 bind 捕获
			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 {
            // 设置值会触发 setData 方法   他们都可以通过 bind 捕获
			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 );
		});
	}
});

这里的实现就显得轻松许多了,只需要多 参数、获取/设置 值进行处理就可以了 ,然后去调用对应的 jQuery.data 方法去实现值的设置或者获取 

之前简单的模拟过一个  原理差不多是这样

proxy 比较简单,但是很容易晕的一个东西,给你来几个 proxy + 返回闭包 基本都会晕 :(

proxy: function( fn, proxy, thisObject ) {
		if ( arguments.length === 2 ) {
			if ( typeof proxy === "string" ) { // 第一种方式调用方式 $.proxy(obj, ‘fn‘)
				thisObject = fn;
				fn = thisObject[ proxy ];
				proxy = undefined;
			} else if ( proxy && !jQuery.isFunction( proxy ) ) { // 第二种调用方式 $.proxy(obj.fn, obj)
				thisObject = proxy;
				proxy = undefined;
			}
		}

		if ( !proxy && fn ) {
            /*
            * 返回重构好 this 的函数, 那么这里的 thisObject 是指向了最终 this 要指向的地方
            */
			proxy = function() {
				return fn.apply( thisObject || this, arguments );
			};
		}

		// Set the guid of unique handler to the same of original handler, so it can be removed
		if ( fn ) {
			proxy.guid = fn.guid = fn.guid || proxy.guid || jQuery.guid++;
		}

		// So proxy can be declared as an argument
		return proxy;
	}

值得关注的就是 这个 thisObject 因为 js 中 函数、对象、数组是传址的,所以 thisObject 能指向你要代理到那个对象的 this 中。感觉非常绕,但是搞明白也就没啥了。随便说下 thisObject 肯定是一个闭包了

var obj = {
          name: ‘tudousi‘,
          test: function() {
            alert( this.name );
            return false;
          }
        };
        var func = $.proxy(obj, ‘test‘);
        $(‘.show‘).click(func);















jQuery剥皮三- data、proxy、event