对v3进行改进,原来是用两个引用数组为元素节点提供UUID,另外两个数组提供缓存体。v4中删掉Data类,只有一个数组来为元素节点提供UUID,再用一个数组提供缓存体。缓存体之上有一个data对象属性,用于保存用户数据。相对于v3与jQuery2.0的新方案,它会在没有私有数据与用户数据时,会把这两个数组对应位置上的元素删掉,防止无限膨胀。
//================================================== // 数据缓存模块 //================================================== define("data", ["$lang"], function($) { var owners = [], caches = []; /** * 为目标对象指定一个缓存体 * @param {Any} owner * @return {Object} 缓存体 * @api private */ function add(owner) { var index = owners.push(owner); return caches[index - 1] = { data: {} }; } /** * 为目标对象读写数据 * @param {Any} owner * @param {Object|String} name ? 要处理的数据或数据包 * @param {Any} data ? 要写入的数据 * @param {Boolean} pvt ? 标识为内部数据 * @return {Any} * @api private */ function innerData(owner, name, data, pvt) { //IE678不能为文本节点注释节点添加数据 var index = owners.indexOf(owner); var table = index === -1 ? add(owner) : caches[index]; var getOne = typeof name === "string" //取得单个属性 var cache = table; //私有数据都是直接放到table中,普通数据放到table.data中 if(!pvt) { table = table.data; } if(name && typeof name == "object") { $.mix(table, name); //写入一组属性 } else if(getOne && data !== void 0) { table[name] = data; //写入单个属性 } if(getOne) { if(name in table) { return table[name]; } else if(!pvt && owner && owner.nodeType == 1) { //对于用HTML5 data-*属性保存的数据, 如<input id="test" data-full-name="Planet Earth"/> //我们可以通过$("#test").data("full-name")或$("#test").data("fullName")访问到 return $.parseData(owner, name, cache); } } else { return table; } } /** * 为目标对象移除数据 * @param {Any} owner * @param {Any} name ? 要移除的数据 * @param {Boolean} pvt ? 标识为内部数据 * @return {Any} * @api private */ function innerRemoveData(owner, name, pvt) { var index = owners.indexOf(owner); if(index > -1) { var delOne = typeof name == "string", table = caches[index], cache = table, clear = 1 if(delOne) { if(!pvt) { table = table.data; } if(table) { delOne = table[name]; delete table[name]; } for(var key in cache) { if(key == "data") { for(var i in cache.data) { clear = 0; break; } } else { clear = 0; break; } } if(clear) { owners.splice(index, 1); caches.splice(index, 1); } } return delOne; //返回被移除的数据 } } $.mix({ //判定是否关联了数据 hasData: function(owner) { return owners.indexOf(owner) > -1; }, // 读写用户数据 data: function(target, name, data) { return innerData(target, name, data); }, //读写内部数据 _data: function(target, name, data) { return innerData(target, name, data, true); }, //移除用户数据 removeData: function(target, name) { return innerRemoveData(target, name); }, //移除内部数据 _removeData: function(target, name) { return innerRemoveData(target, name, true); }, //将HTML5 data-*的属性转换为更丰富有用的数据类型,并保存起来 parseData: function(target, name, cache, value) { var data, key = $.String.camelize(name), _eval if(cache && (key in cache)) return cache[key]; if(arguments.length != 4) { var attr = "data-" + name.replace(/([A-Z])/g, "-$1").toLowerCase(); value = target.getAttribute(attr); } if(typeof value === "string") { //转换 /^(?:\{.*\}|null|false|true|NaN)$/ if(/^(?:\{.*\}|\[.*\]|null|false|true|NaN)$/.test(value) || +value + "" === value) { _eval = true; } try { data = _eval ? eval("0," + value) : value; } catch(e) { data = value; } if(cache) { cache[key] = data; } } return data; }, //合并数据 mergeData: function(cur, src) { if($.hasData(cur)) { var oldData = $._data(src), curData = $._data(cur), events = oldData.events; $.Object.merge(curData, oldData); if(events) { curData.events = []; for(var i = 0, item; item = events[i++];) { $.event.bind(cur, item); } } } } }); return $ });