缓存系统我以前在博客写过了,此后我对javascript的哲学发生很大的改变。以前是尽量避免对原型进行扩展的,但反过来一想,有什么关系?除非是傻子或特别菜的人才会混用多个库,能混用库的人也只有高手才能避免命名冲突的尴尬。十大类库中,前面几个都是对原型进行疯狂扩展的。像mootools,代码不多,但能实在比jQuery多许多的功能。这就得益于原型扩展带来的代码量的减少。当然,我们还是要避免对Object进行扩展,这个真的是牵一发动全身。
在我们开始时先列出一些要用到的工具(突然想起烹饪节目中的材料介绍……orz)
dom.mixin = function(result, source) { if (arguments.length === 1) { source = result; result = dom; } if (result && source ){ for(var key in source) source.hasOwnProperty(key) && (result[key] = source[key]); } if(arguments.length > 2 ){ var others = [].slice.call(arguments,2); for(var i=0,n=others.length;i<n;i++){ result = arguments.callee(result,others[i]); } } return result; }
dom.mixin = function(result, source) { if (arguments.length === 1) { source = result; result = dom; } if (result && source ){ for(var key in source) source.hasOwnProperty(key) && (result[key] = source[key]); } if(arguments.length > 2 ){ var others = [].slice.call(arguments,2); for(var i=0,n=others.length;i<n;i++){ result = arguments.callee(result,others[i]); } } return result; } var fn = "prototype", toString = Object[fn].toString; dom.mixin({ //类型识别 is : function (obj,type) { var undefined; return (type === "Null" && obj === null) || (type === "Undefined" && obj === undefined ) || toString.call(obj).slice(8,-1) === type; }, //分支对象构建 oneObject : function(arr,val){ var result = {},value = val !== undefined ? val :1; for(var i=0,n=arr.length;i<n;i++) result[arr[i]] = value; return result; }, deepCopy:function(result, source){ for(var key in source) { var copy = source[key]; if(result === copy) continue; if(dom.is(copy,"Object")){ result[key] = arguments.callee(result[key] || {}, copy); }else if(dom.is(copy,"Array")){ result[key] = arguments.callee(result[key] || [], copy); }else{ result[key] = copy; } } return result; }, //检测是否为空对象,如果第二参数为true,则只检测本地属性 //此方法对没有hasOwnProperty属性的对象无效 isEmptyObject: function(obj,local ) { for ( var key in obj ) { if (!!local){ if(obj.hasOwnProperty(key)) return false; }else{ return false; } } return true; } });
下面是缓存系统:
var noData = dom.oneObject(["embed","object","applet"]), uuid = 1,windowdata = {},expando = "dom" + (Math.random()+"").slice(-8); dom.mixin({ cache: {}, expando:expando, //读写缓存体 store: function( target, name, data ) { if ( target.nodeName && noData[target.nodeName.toLowerCase()] ) { return; } target = target == window ? windowData :target; //给目标对象设置一个自定义属性,注意这属性名是随机的, var key , cache = dom.cache, thisCache; //如果是第一次访问,目标对象没有代表uuid的自定属性,需要设置一个。 //注意这个属性每次刷新页面都是不一样的 //以uuid作为访问缓存体的钥匙,如果没有则设置一个,其值恒为一个对象 if(!(expando in target)){ key = target[ expando ] = uuid++; thisCache = cache[ key ] = {}; }else{ key = target[expando]; thisCache = cache[ key ]; } //通过Object一次把许多东西写入 if ( dom.is(name,"Object") ) { thisCache = dom.deepCopy(thisCache, name); //如果name为字符串并且data不为undefined,则每一次只写入一个键值对 } else if ( data !== undefined ) { thisCache[ name ] = data; } return dom.is(name,"String") ? thisCache[ name ] : thisCache; }, //移除缓存体 unstore: function( target, name ) { if ( target.nodeName && noData[target.nodeName.toLowerCase()] ) { return; } target = target == window ? windowData : target; if(!(expando in target)){ return } var key = target[ expando ], cache = dom.cache, thisCache = cache[ key ]; //如果传入两个参数, if ( name ) { if ( thisCache ) { //移除指定的条目 delete thisCache[ name ]; //如果缓存体为空,留着也没用,删掉 if ( dom.isEmptyObject(thisCache,true) ) { arguments.callee( target ); } } //如果只传入一个参数,那么连目标对象上的自定义属性也一同移除 } else { //注意,IE8,FF等新锐浏览器的元素节点利用removeAttribute是无法把 //通过 "."这种圆点寻址风格设置的属性删除掉的,必须使用delete操作符才行 try{ delete target[ expando ]; }catch(e){ //IE6、IE7则恰恰相反,使用delete操作符来删除属性会报错,必须使用removeAttribute方法 target.removeAttribute( expando ); } // 缓存体上的数据 delete cache[ key ]; } } });
这里有一个细节就是删除元素节点的自定义属性,IE6IE7要特殊处理一下,一般而言直接通过 "."这种圆点寻址风格或数组标记风格来设置的都叫属性,而通过setAttribute设置的才是特性。而IE6IE7恰恰要用removeAttribute移除才行。这再次一次证明了其属性特性不分的糟糕实现。
不过,还有个问题,如果我们要传入一组数据呢?然后每次添加都是在这个数组中操作,原来的系统就不灵光了。以前我的缓存系统是通过判定属性名来确定是储存单个数据还是列表还是映射。但翻译ECMA时,有句话是这样说的,我们不应该在原函数的参数上做文章来实现新的功能,而是新建一个函数来实现它。这样做的好处是易维护。jQuery就是这样做的,我们跟着模仿就是。模仿多了自然会创新。
相关链接: