模仿jQuery,設計了一個緩衝系統。像jQuery.data這樣的東西,Prototype與mootools也有,目的都是用來輔助事件系統,用來緩衝其中產生的資料,而非緩衝普通函數上次計算的結果。Prototype利用了它的Hash類,mootools沒細看,它內部好像用來緩衝uuid。一個共識是,為頁面用到的元素設定uuid非常有用,要尋找元素時,可以避免重複尋找,也可以用於與事件回呼函數相綁定。由於uuid目前只有IE支援,它叫做uniqueID,格式為ms__id\d+,後面的數字也有名堂,叫做uniqueNumber。jQuery那個算是uniqueNumber吧,而且它的緩衝系統非常複雜,支援緩衝單個資料(利用data這個讀寫方法)與一組資料(利用queue,刪除用dequeue)。沒辦法,因為它是白手起家,沒有像Prototype那樣利用一個自定資料類型分擔一下職責。是時候進入正題,說一下我的緩衝系統了。它利用到我的超級數組對象,實現像queue與dequeue。但我的超級數組對象能做的事更多,像filter,forEach,map,reduce,one,toObject,contains,remove等一應俱全。
dom.eventTypes = dom.array(String("abort blur change click contextmenu \ dblclick error focus keydown keypress keyup load mousedown \ mouseenter mouseup mouseleave mousemove mouseover mouseout \ reset resize select submit unload").match(/\w+/g)); //******************************緩衝系統*********************** dom.mixin({ uuid : 0, storage: {}, buildCache:function(item){ var key,cache = dom.storage; //如果是window if ( item.setInterval && ( item !== window && !item.frameElement )) { key = "dom-window" }else if(item.nodeType){ if(!item.uuid){ item.uuid = "dom" + dom.uuid++ dom.cache("uuid","uuid-set",item);//儲存元素的引用 }//如果當前元素沒有uuid這屬性,那麼為它添加一個 key = item.uuid }else if(dom.isString(item)){ key = item; }else{ throw "item must be element node ,window or string" } if(!cache[key]){ cache[key] = {}; } return cache[key] }, cache:function(item,name,data){//讀寫緩衝 var cache = dom.buildCache(item); if(data !== undefined){ if(dom.isFunction(data)){//緩衝函數,在函數設定一個uuid,產生方式取其toString()並去掉空白 var uuid = (data+"").replace(/\s+/g,''); data.uuid = uuid; } if(!cache[name]){ //set與list允許緩衝一組資料, //其中如果帶有-set尾碼允許其中的元素重複,list則不允許 if(/-set$/.test(name)){//如果是第一次儲存 cache[name] = array(data); cache[name].type = "set"; }else if(/-list$/.test(name)){// cache[name] = array(data); cache[name].type = "list"; }else{ cache[name] = data; } }else{ var type = cache[name].type; if(type && type === "set" ){ cache[name].ensure(data) }else if(type && type === "list" ){ cache[name].push(data) }else{ cache[name] = data; } } return item; }else{ return cache[name] } }, //參數可以為1,2,3 removeCache:function(item,name,data){ if(!arguments.length) return; var cache = dom.buildCache(item),isDOM = !!item.nodeType; if(isDOM && dom.eventTypes.contains(name)){ name = name + "-handlers-list"; }; if(arguments.length === 3){//移除指定的data if(cache[name] instanceof dom.array && cache[name].length > 0){ //對於由事件綁定函數構成的超級數組,我們在緩衝它的時候為每個函數設定了一個uuid //現在我們比較現在傳入的函數的uuid與超級數組中的某個元素的uuid是否相等 //如果有我們將得它的引用,有了它我們才能使用remove方法移除它。 if(dom.isFunction(data)){ var uuid = (data+"").replace(/\s+/g,''), fn = cache[name].one(function(fn){ return fn.uuid == uuid; }); if(fn){ cache[name].remove(fn); } }else{ cache[name].remove(data); } if(cache[name].length === 0) dom.removeCache(item,name); }else{ dom.removeCache(item,name); } }else if(arguments.length === 2){//移除條目 delete cache[name] if(!dom.isExtensible(cache)){ dom.removeCache(item); } }else { delete cache; if(isDOM){ dom.removeCache("uuid","uuid-set",item); dom.removeAttr(item,"uuid"); } } }, clearCache:function(){ var cache = dom.buildCache("uuid","uuid-set"); if(!cache) return;//如果為空白直接返回 cache.forEach(function(el){ dom.removeAttr(el,"uuid"); }); dom.storage = null; } });
buildCache是用於在dom.storage上開闢一個命名空間,cache用於在命名空間儲存各種資料,可以為單個資料,一組資料,一組資料又分為list與set兩種,區別是允許元素是否重複,重複的list相重於jQuery.queue設定的數組。對於window對象的處理,jQuery是在閉包內搞了一個windowData,我則它為在dom.storage開闢了一個名為dom-window的空間。removeCache相當於jQuery.removeData與jQuery.unquere,但功能還要強大,能根據傳入參數的個數選用不同的功能。clearCache則直接刪除所有緩衝。
值得注意的是,在IE中為DOM元素設定私人屬性會引起記憶體流失,所有在頁面unload時需要手動去除它們。各大類庫也是這樣做了,dom.cache("uuid","uuid-set",item)就是用於unload時取出所有設定過uuid的元素,一一去掉其uuid。不過實際上,調用clearCache方法就可以了。