读Zepto源码之Data模块
Zepto
的 Data
模块用来获取 DOM
节点中的 data-*
属性的数据,和储存跟 DOM
相关的数据。
读 Zepto 源码系列文章已经放到了github上,欢迎star: reading-zepto
源码版本
本文阅读的源码为 zepto1.2.0
GitBook
内部方法
attributeData
var data = {}, dataAttr = $.fn.data, camelize = $.camelCase,
exp = $.expando = 'Zepto' + (+new Date()), emptyArray = []
function attributeData(node) {
var store = {}
$.each(node.attributes || emptyArray, function(i, attr){
if (attr.name.indexOf('data-') == 0)
store[camelize(attr.name.replace('data-', ''))] =
$.zepto.deserializeValue(attr.value)
})
return store
}
这个方法用来获取给定 node
中所有 data-*
属性的值,并储存到 store
对象中。
node.attributes
获取到的是节点的所有属性,因此在遍历的时候,需要判断属性名是否以 data-
开头。
在存储的时候,将属性名的 data-
去掉,剩余部分转换成驼峰式,作为 store
对象的 key
。
在 DOM
中的属性值都为字符串格式,为方便操作,调用 deserializeValue
方法,转换成对应的数据类型,关于这个方法的具体分析,请看 《读Zepto源码之属性操作》
setData
function setData(node, name, value) {
var id = node[exp] || (node[exp] = ++$.uuid),
store = data[id] || (data[id] = attributeData(node))
if (name !== undefined) store[camelize(name)] = value
return store
}
更多时候,储存数据不需要写在 DOM
中,只需要储存在内存中即可。而且读取 DOM
的成本非常高。
setData
方法会将对应 DOM
的数据储存在 store
对象中。
var id = node[exp] || (node[exp] = ++$.uuid)
首先读取 node
的 exp
属性,从前面可以看到 exp
是一个 Zepto
加上时间戳的字符串,以确保属性名的唯一性,避免覆盖用户自定义的属性,如果 node
尚未打上 exp
标记,表明这个节点并没有缓存的数据,则设置节点的 exp
属性。
store = data[id] || (data[id] = attributeData(node))
从 data
中获取节点之前缓存的数据,如果之前没有缓存数据,则调用 attributeData
方法,获取节点上所有以 data-
开头的属性值,缓存到 data
对象中。
store[camelize(name)] = value
最后,设置需要缓存的值。
getData
function getData(node, name) {
var id = node[exp], store = id && data[id]
if (name === undefined) return store || setData(node)
else {
if (store) {
if (name in store) return store[name]
var camelName = camelize(name)
if (camelName in store) return store[camelName]
}
return dataAttr.call($(node), name)
}
}
获取 node
节点指定的缓存值。
if (name === undefined) return store || setData(node)
如果没有指定属性名,则将节点对应的缓存全部返回,如果缓存为空,则调用 setData
方法,返回 node
节点上所有以 data-
开头的属性值。
if (name in store) return store[name]
如果指定的 name
在缓存 store
中,则将结果返回。
var camelName = camelize(name)
if (camelName in store) return store[camelName]
否则,将指定的 name
转换成驼峰式,再从缓存 store
中查找,将找到的结果返回。这是兼容 camel-name
这样的参数形式,提供更灵活的 API
。
如果缓存中都没找到,则回退到用 $.fn.data
查找,其实就是查找 data-
属性上的值,这个方法后面会分析到。
DOM方法
.data()
$.fn.data = function(name, value) {
return value === undefined ?
$.isPlainObject(name) ?
this.each(function(i, node){
$.each(name, function(key, value){ setData(node, key, value) })
}) :
(0 in this ? getData(this[0], name) : undefined) :
this.each(function(){ setData(this, name, value) })
}
data
方法可以设置或者获取对应 node
节点的缓存数据,最终分别调用的是 setData
和 getData
方法。
分析这段代码,照例还是将三元表达式一个一个拆解,来看看都做了什么事情。
value === undefined ? 三元表达式 : this.each(function(){ setData(this, name, value) })
先看第一层,当有传递 name
和 value
时,表明是设置缓存,遍历所有元素,分别调用 setData
方法设置缓存。
$.isPlainObject(name) ?
this.each(function(i, node){
$.each(name, function(key, value){ setData(node, key, value) })
}) : 三元表达式
data
的第一个参数还支持对象的传值,例如 $(el).data({key1: 'value1'})
。如果是对象,则对象里的属性为需要设置的缓存名,值为缓存值。
因此,遍历所有元素,调用 setData
设置缓存。
0 in this ? getData(this[0], name) : undefined
最后,判断集合是否不为空( 0 in this
), 如果为空,则直接返回 undefined
,否则,调用 getData
,返回第一个元素节点对应 name
的缓存。
.removeData()
$.fn.removeData = function(names) {
if (typeof names == 'string') names = names.split(/\s+/)
return this.each(function(){
var id = this[exp], store = id && data[id]
if (store) $.each(names || store, function(key){
delete store[names ? camelize(this) : key]
})
})
}
removeData
用来删除缓存的数据,如果没有传递参数,则全部清空,如果有传递参数,则只删除指定的数据。
names
可以为数组,指定需要删除的一组数据,也可以为以空格分割的字符串。
if (typeof names == 'string') names = names.split(/\s+/)
如果检测到 names
为字符串,则先将字符串转换成数组。
return this.each(function(){
var id = this[exp], store = id && data[id]
...
})
遍历元素,对所有的元素都进行删除操作,找出和元素对应的缓存 store
。
if (store) $.each(names || store, function(key){
delete store[names ? camelize(this) : key]
})
如果 names
存在,则删除指定的数据,否则将 store
缓存的数据全部删除。
.remove()和.empty()方法的改写
;['remove', 'empty'].forEach(function(methodName){
var origFn = $.fn[methodName]
$.fn[methodName] = function() {
var elements = this.find('*')
if (methodName === 'remove') elements = elements.add(this)
elements.removeData()
return origFn.call(this)
}
})
原有的 remove
和 empty
方法,都会有 DOM
节点的移除,在移除 DOM
节点后,对应节点的缓存数据也就没有什么意义了,所有在移除 DOM
节点后,也需要将节点对应的数据也清空,以释放内存。
var elements = this.find('*')
if (methodName === 'remove') elements = elements.add(this)
elements
为所有下级节点,如果为 remove
方法,则节点自身也是要被移除的,所以需要将自身也加入到节点中。
最后调用 removeData
方法,不传参清空所有数据,在清空数据后,再调用原来的方法移除节点。
工具方法
$.data
$.data = function(elem, name, value) {
return $(elem).data(name, value)
}
data
最后调用的也就是 DOM
的 data
方法。
$.hasData
$.hasData = function(elem) {
var id = elem[exp], store = id && data[id]
return store ? !$.isEmptyObject(store) : false
}
判断某个元素是否已经有缓存的数据。
首先通过从缓存 data
中,取出对应 DOM
的缓存 store
,如果 store
存在,并且不为空,则返回 true
,其实情况返回 false
。
系列文章
- 读Zepto源码之代码结构
- 读Zepto源码之内部方法
- 读Zepto源码之工具函数
- 读Zepto源码之神奇的$
- 读Zepto源码之集合操作
- 读Zepto源码之集合元素查找
- 读Zepto源码之操作DOM
- 读Zepto源码之样式操作
- 读Zepto源码之属性操作
- 读Zepto源码之Event模块
- 读Zepto源码之IE模块
- 读Zepto源码之Callbacks模块
- 读Zepto源码之Deferred模块
- 读Zepto源码之Ajax模块
- 读Zepto源码之Assets模块
- 读Zepto源码之Selector模块
- 读Zepto源码之Touch模块
- 读Zepto源码之Gesture模块
- 读Zepto源码之IOS3模块
- 读Zepto源码之Fx模块
- 读Zepto源码之fx_methods模块
- 读Zepto源码之Stack模块
- 读Zepto源码之Form模块
附文
参考
License
署名-非商业性使用-禁止演绎 4.0 国际 (CC BY-NC-ND 4.0)
作者:对角另一面
读Zepto源码之Data模块的更多相关文章
- 读Zepto源码之Callbacks模块
Callbacks 模块并不是必备的模块,其作用是管理回调函数,为 Defferred 模块提供支持,Defferred 模块又为 Ajax 模块的 promise 风格提供支持,接下来很快就会分析到 ...
- 读Zepto源码之Ajax模块
Ajax 模块也是经常会用到的模块,Ajax 模块中包含了 jsonp 的现实,和 XMLHttpRequest 的封装. 读 Zepto 源码系列文章已经放到了github上,欢迎star: rea ...
- 读Zepto源码之assets模块
assets 模块是为解决 Safari 移动版加载图片过大过多时崩溃的问题.因为没有处理过这样的场景,所以这部分的代码解释不会太多,为了说明这个问题,我翻译了<How to work arou ...
- 读Zepto源码之Deferred模块
Deferred 模块也不是必备的模块,但是 ajax 模块中,要用到 promise 风格,必需引入 Deferred 模块.Deferred 也用到了上一篇文章<读Zepto源码之Callb ...
- 读Zepto源码之Selector模块
Selector 模块是对 Zepto 选择器的扩展,使得 Zepto 选择器也可以支持部分 CSS3 选择器和 eq 等 Zepto 定义的选择器. 在阅读本篇文章之前,最好先阅读<读Zept ...
- 读Zepto源码之Touch模块
大家都知道,因为历史原因,移动端上的点击事件会有 300ms 左右的延迟,Zepto 的 touch 模块解决的就是移动端点击延迟的问题,同时也提供了滑动的 swipe 事件. 读 Zepto 源码系 ...
- 读Zepto源码之Gesture模块
Gesture 模块基于 IOS 上的 Gesture 事件的封装,利用 scale 属性,封装出 pinch 系列事件. 读 Zepto 源码系列文章已经放到了github上,欢迎star: rea ...
- 读Zepto源码之IOS3模块
IOS3 模块是针对 IOS 的兼容模块,实现了两个常用方法的兼容,这两个方法分别是 trim 和 reduce . 读 Zepto 源码系列文章已经放到了github上,欢迎star: readin ...
- 读Zepto源码之Fx模块
fx 模块为利用 CSS3 的过渡和动画的属性为 Zepto 提供了动画的功能,在 fx 模块中,只做了事件和样式浏览器前缀的补全,没有做太多的兼容.对于不支持 CSS3 过渡和动画的, Zepto ...
随机推荐
- 201521123005 《Java程序设计》 第十一周学习总结
1. 本周学习总结 1.1 以你喜欢的方式(思维导图或其他)归纳总结多线程相关内容. 2. 书面作业 本次PTA作业题集多线程 Q1.互斥访问与同步访问 完成题集4-4(互斥访问)与4-5(同步访问) ...
- 201521123039 《java程序设计》第一周学习总结
#1.本章学习总结 Java是面向对象的程序语言,它一切定义都是对象.我们所编写的Java程序经过编译后生成了*.class的文件,再经过JVM对*.class解释运行就可以得到Java程序,所以Ja ...
- linux下修改rm命令防止误删除
前言:相信很多朋友都遇到过在linux下用rm命令误删除文件的时候,此刻的心中仿佛有无数的羊驼在奔腾.那么怎么防止这种情况发生呢?当然是有方法的,我们可以写一个shell脚本,改变一下rm命令的作用. ...
- Python: 列表注意细节与元组的基本用法
列表注意细节: 1.list.clear():将列表中成员清空(与del list区别开) 2.list.copy():复制一份相同的列表(浅COPY,只复制列表第一层) 3.如果两个列表相等,如li ...
- spring mvc 经常出现的错误
spring mvc 经常出现的错误 spring3.0 和jdk 1.8不是很兼容.有时候会出现一些错误 建议使用spring 4.0和jdk1.8搭配使用 书籍 spring mvc 学习指南上面 ...
- 自己把jar包添加到maven仓库中
定制库到Maven本地资源库 这里有2个案例,需要手动发出Maven命令包括一个 jar 到 Maven 的本地资源库. 要使用的 jar 不存在于 Maven 的中心储存库中. 您创建了一个自定义的 ...
- Maven简介(一)
在现实的企业中,以低成本.高效率.高质量的完成项目,不仅仅需要技术大牛,企业更加需要管理大牛,管理者只懂技术是远远不够的.当然,管理可以说有很多的方面,例如:对人员的管理,也有对项目的管理等等.如果你 ...
- 常见注入手法第一讲EIP寄存器注入
常见注入手法第一讲EIP寄存器注入 博客园IBinary原创 博客连接:http://www.cnblogs.com/iBinary/ 转载请注明出处,谢谢 鉴于注入手法太多,所以这里自己整理一下, ...
- PuTsangTo-单撸游戏开发01 Flag与计划
先立下flag,至少1年之内坚持并2年之内完成自己的一个梦想--游戏开发. 没有参加培训也不打算参加培训,就纯靠业余时间自学并用自己的思路完成一整套游戏体系.做出此决心时也已经做好准备烂尾了,但是有种 ...
- shell脚本获取文件中key/value的小方法
方法有N种,awk.sad.grep.cut... 以上几种方式不写了,就写两个不太常用到的. 废话少说,直接上代码: cat a.txt aa.gif=aaaa.gif bb.gif=bbbb.gi ...