jquery1.4 jquery1.4下載node
這裏使用了 jQuery1.4,爲何使用 1.4 由於 1.4 不少特性沒有添加分析起來相對容易。jquery
這個 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 實現的方法app
實現方法就是 jQuery 在對元素操做的時候會賦值一個 "jQuery" + now() 時間戳的屬性,其值是一個 uuid 惟一的數字,每次操做會加 1 ,而後在 jQuery 的 cache 的對象靜態變量中根據這個 uuid 賦值一個 key 就像這樣 {5:{}}。而且把你要加入的 data 放去這個 key 爲 5 的對象中 {5:{tudou:"abc"}}。他們惟一關聯的就是這個 uuid 。less
看看 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 方法去實現值的設置或者獲取 ui
以前簡單的模擬過一個 原理差很少是這樣this
proxy 比較簡單,可是很容易暈的一個東西,給你來幾個 proxy + 返回閉包 基本都會暈 :(spa
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);