深入jQuery中的data()
引入
data函数在jQuery中看起来很不起眼, 就像沙滩上一颗平凡的沙子, 但仔细一瞅, 却惊讶的发现data是jQuery中无比重要的一环, 甚至jQuery中各种事件都基于此。
data有什么作用?
在我们平时js编码过程中,我们经常会向DOM元素中添加各种自定义属性,这样有一个弊端。
1 假设我们在DOM元素中添加了一个属性,这个属性指向了某个js对象。 dom1.ele = jsObj
2 当这个js对象发挥完作用后,我们已经用不到他了。这时候按理说应该把这个js变量清空,释放内存。大家都知道,如果一个js对象不存在任何外在引用的话,解释器会自动将其在内存中删除,这也是javascript相对于c++等手动管理内存的程序的优点。
3 但是这时候问题来了,因为DOM元素引用了这个js对象,尽管这个js对象已经没有存在的意义了,但是解释器是不会把他删除的。如果想要把其删除,我们可能需要将DOM元素的这个属性设置为null。
4 我们编写了这么多的代码,哪里能把 每个js对象是不是被DOM元素引用了都记住啊?
5 而且,假如DOM元素与js对象之间相互循环引用,根本就无法删除! 这就是内存泄漏
6 所以,为了避免这种情况的发生,我们要尽量避免 引用数据(这里的引用数据可以说是javascript对象) 直接依附在DOM对象上。
7 data就是用来搞定以上问题的方法。
data是如何搞定以上问题的?
首先来说一说jQuery中Data实现的大体思路:
1 首先我们创建一个数据缓存池,这个缓存池专门用来存储 向 DOM对象或者jQuery对象附加的额外数据。
2 当我们要向DOM对象或者jQuery对象附加额外数据的时候,我们附加的数据其实是保存于这个缓存池中
3 DOM对象或者jQuery对象生成一个额外属性,这个属性保存了 附加数据在缓存池中的‘门牌号’(位置或者索引)
4 当我们访问DOM对象或者jQuery对象的附加数据时,实际上是先取得其附加数据的门牌号,然后找到缓存池中对应门牌号的数据,进行操作。
大体思路讲完,那么来分析一下具体思路:
在jQuery中,有一个Data构造函数,每当运行这个构造函数时,就会生成一个实例。jQuery默认会自动生成两个Data实例:
var dataPriv = new Data() jQuery私有的,我们尽量不要对这个实例进行操作。
var dataUser = new Data() 这个就是服务于用户了,我们使用data()方法都是对这个实例进行操作。
所有的Data实例都有以下属性:
expando: 值为字符串类型,每个Data实例的expando属性的值都不相同,用来区分不同的Data实例,类似于id的作用,expando的值就是上文中的额外属性。
uid: 这就是上文中的门牌号,初始为1,随着不同对象的附加数据的加入,自增长。
cache : 一个对象 {} ,这就是缓存池了。
来个实例:
$(document.body).data('aaa', 'value-aaa') console.dir(document.body)
body对象有一个名为jquer210023......的额外属性,
这个属性的名称就是dataUser的expando的值
这个属性的值就是门牌号。
总结: data实际上就是对js对象或者DOM对象的额外属性做了一个集中的管理。对于那些不会产生内存泄漏的额外数据,我们也可以直接向js对象或者DOM对象附加。
好,理清楚上面的关系后,我们再来看一下源码:
define(["../core","../var/rnotwhite","./accepts" ], function( jQuery, rnotwhite ) {function Data() {// Support: Android<4,// Old WebKit does not have Object.preventExtensions/freeze method,// return new empty object instead with no [[set]] accessorObject.defineProperty( this.cache = {}, 0, {get: function() {return {};}});// jQuery.expando = "jQuery" + ( version + Math.random() ).replace( /\D/g, "" ) expando是一个jQuery的唯一标示// 格式是:'jQuery\\d*' 也就是'jQuery'+ 多个数字。这里为啥要搞得这么麻烦呢?// 应因为我们可能会创建多个Data对象,为了保证每个Data对象的expando属性的值不相等,所以这么搞this.expando = jQuery.expando + Math.random(); }Data.uid = 1; // Data函数的属性,'静态属性' Data.accepts = jQuery.acceptData;Data.prototype = {key: function( owner ) {// We can accept data for non-element nodes in modern browsers,// but we should not, see #8335.// Always return the key for a frozen object.// 若owner在该缓存池中存在对应的缓存对象,则返回混存对象的key(是一个数字),// 若owner在该缓存池中不存在对应的缓存对象,则在缓存池中为其创建一个缓存对象,并返回该缓存对象的keyif ( !Data.accepts( owner ) ) {return 0;}var descriptor = {},// Check if the owner object already has a cache key// 检查owner对象在该缓存池中是否存在缓存unlock = owner[ this.expando ]; // 是一个数字,用来作为缓存池中缓存对象的key// If not, create one// 如果没有,则创建一个if ( !unlock ) {unlock = Data.uid++;// Secure it in a non-enumerable, non-writable property// 给owner附加一个属性 owner[this.expando] = unlock ,并且该属性不能被枚举,try {descriptor[ this.expando ] = { value: unlock };Object.defineProperties( owner, descriptor );// Support: Android<4// Fallback to a less secure definition} catch ( e ) {descriptor[ this.expando ] = unlock;jQuery.extend( owner, descriptor );}}// Ensure the cache object// 确保owner对应的缓存对象已存在if ( !this.cache[ unlock ] ) {this.cache[ unlock ] = {};}// 返回unlockreturn unlock;},set: function( owner, data, value ) {// 设置owner对应的缓存对象var prop,// There may be an unlock assigned to this node,// if there is no entry for this "owner", create one inline// and set the unlock as though an owner entry had always existedunlock = this.key( owner ), // 获取owner的对应的缓存对象在缓存池中的key(这里的key,是键值对中的键的意思)cache = this.cache[ unlock ]; // 获取owner所对应的缓存对象// Handle: [ owner, key, value ] args// 根据传入参数的个数以及类型实现重载if ( typeof data === "string" ) {cache[ data ] = value;// Handle: [ owner, { properties } ] args} else {// Fresh assignments by object are shallow copiedif ( jQuery.isEmptyObject( cache ) ) {jQuery.extend( this.cache[ unlock ], data );// Otherwise, copy the properties one-by-one to the cache object} else {for ( prop in data ) {cache[ prop ] = data[ prop ];}}}// 返回缓存对象return cache;},get: function( owner, key ) {// 获取owner对象的名为key的属性值// owner:是一个对象(可以是jQuery对象也可以是DOM对象) key: 属性名// Either a valid cache is found, or will be created.// New caches will be created and the unlock returned,// allowing direct access to the newly created// empty data object. A valid owner object must be provided.var cache = this.cache[ this.key( owner ) ]; // owner的缓存对象return key === undefined ? cache : cache[ key ]; // 没指定key的话就返回整个缓存对象,若指定了key则返回在该缓存对象的key属性的值 },access: function( owner, key, value ) {var stored;// In cases where either:// // 1. No key was specified 没有指定key// 2. A string key was specified, but no value provided 指定了字符串格式的key,但没有指定value// // Take the "read" path and allow the get method to determine// which value to return, respectively either:// // 1. The entire cache object 整个缓存对象// 2. The data stored at the key 缓存对象中某个键的值// if ( key === undefined || // 没有指定key或者指定了字符串格式的key,但没有指定value((key && typeof key === "string") && value === undefined) ) {// 没有指定key:获取整个缓存对象// 指定了字符串格式的key,但没有指定value: 获取缓存对象中key的值stored = this.get( owner, key );return stored !== undefined ?stored : this.get( owner, jQuery.camelCase(key) );}// [*]When the key is not a string, or both a key and value// are specified, set or extend (existing objects) with either:// 当key不是一个字符串,或者key和value都指定了,就会根据情况进行设置或者扩展// // 1. An object of properties// 2. A key and value// this.set( owner, key, value );// Since the "set" path can have two possible entry points// return the expected data based on which path was taken[*]return value !== undefined ? value : key;},remove: function( owner, key ) {// 清空owner对应的缓存对象,或者移除缓存对象中的某个键值对var i, name, camel,unlock = this.key( owner ),cache = this.cache[ unlock ];// 如果没有指定key,则清空缓存对象if ( key === undefined ) {this.cache[ unlock ] = {};} else {// Support array or space separated string of keysif ( jQuery.isArray( key ) ) {// If "name" is an array of keys...// When data is initially created, via ("key", "val") signature,// keys will be converted to camelCase.// Since there is no way to tell _how_ a key was added, remove// both plain key and camelCase key. #12786// This will only penalize the array argument path.name = key.concat( key.map( jQuery.camelCase ) );} else {camel = jQuery.camelCase( key );// Try the string as a key before any manipulationif ( key in cache ) {name = [ key, camel ];} else {// If a key with the spaces exists, use it.// Otherwise, create an array by matching non-whitespacename = camel;name = name in cache ?[ name ] : ( name.match( rnotwhite ) || [] );}}i = name.length;while ( i-- ) {delete cache[ name[ i ] ];}}},hasData: function( owner ) {// 检查owner在该缓存池中是否存在缓存对象return !jQuery.isEmptyObject(this.cache[ owner[ this.expando ] ] || {});},discard: function( owner ) {if ( owner[ this.expando ] ) {delete this.cache[ owner[ this.expando ] ];}} };return Data; });
Data构造函数源码解析
可能会有同学问道:如果我想对dataPriv进行操作该如何?
请看源码:
jQuery.extend({hasData: function( elem ) {return dataUser.hasData( elem ) || dataPriv.hasData( elem );},data: function( elem, name, data ) {return dataUser.access( elem, name, data );},removeData: function( elem, name ) {dataUser.remove( elem, name );},// TODO: Now that all calls to _data and _removeData have been replaced// with direct calls to dataPriv methods, these can be deprecated._data: function( elem, name, data ) {return dataPriv.access( elem, name, data );},_removeData: function( elem, name ) {dataPriv.remove( elem, name );} });
通过源码,我们可以看出:
jQuery.data() jQuery.remove()都是对dataUser进行操作,而jQuery._data() jQuery._remove()都是对dataPriv进行操作。
理解jQuery.data(ele,name,data) 与 jQuery().data(key,value)的不同。
通过上面的源码,我们可以看到jQuery.data(ele,name,data)是对ele元素附加数据。
而jQuery().data(key,value)则会为jQuery对象中的所有DOM对象分别附加数据
来看源码(删减了部分):
jQuery.fn.extend({data: function( key, value ) {var i, name, data,elem = this[ 0 ],attrs = elem && elem.attributes;return access( this, function( value ) {var data,camelKey = jQuery.camelCase( key ); // 从这里可以看出,为jQuery对象中的每个DOM元素分别附加数据this.each(function() {// First, attempt to store a copy or reference of any// data that might've been store with a camelCased key.var data = dataUser.get( this, camelKey );// For HTML5 data-* attribute interop, we have to// store property names with dashes in a camelCase form.// This might not apply to all properties...*dataUser.set( this, camelKey, value );// *... In the case of properties that might _actually_// have dashes, we need to also store a copy of that// unchanged property.if ( key.indexOf("-") !== -1 && data !== undefined ) {dataUser.set( this, key, value );}});}, null, value, arguments.length > 1, null, true );},removeData: function( key ) {return this.each(function() {dataUser.remove( this, key );});} });
-----------------------------------------------分隔线---------------------------------------------------
上文中的所有源码:为jQuery.1.12
转载于:https://www.cnblogs.com/MnCu8261/p/6105103.html
深入jQuery中的data()相关推荐
- 理解JQuery中的data()使用方法
在前端我们经常会做的操作就是做数据状态的判断和数据处理.提交,经常会操作dom,也会保存一个全局的数据处理.这样做是可以实现很多功能,但是缺点就是过多操作dom会浪费性能,全局数据保存多了有时候真的会 ...
- 关于Jquery中ajax方法data参数用法的总结
关于Jquery中ajax方法data参数用法的总结 jquery手册描述: 示例: $.ajax({type: "POST",url: "some.php", ...
- jquery ajax传递data,基于jquery ajax中的data传递参数与后台工作的流程
基于jquery ajax中的data传递参数与后台工作的流程 2014-11-12 分类: php学习,前端资源,学习jquery基础 首先列出实例代码 js代码: $('button').c ...
- jQuery 中 data 方法的实现原理
jQuery 中 data 方法的实现原理 前言:jQuery 作为前端使用最多最广泛的 JS 库,其源码每个 JSer 都应该研究一下.早就打算看却一直被各种事拖着,上次某公司面试时被问到 jQue ...
- 关于JQuery中的ajax请求或者post请求的回调方法中的操作执行或者变量修改没反映的问题...
前段时间做一个项目,而项目中所有的请求都要用jquery 中的ajax请求或者post请求,但是开始处理一些简单操作还好,但是自己写了一些验证就出现问题了,比如表单提交的时候,要验证帐号的唯一性,所以 ...
- jQuery中常用的函数方法总结
jQuery中为我们提供了很多有用的方法和属性,自己总结的一些常用的函数,方法.个人认为在www.21kaiyun.com的紫微斗数星座在线排盘开发中会比较常用的,仅供大家学习和参考. 事件处理 re ...
- JQuery中的事件以及动画
嘿嘿,今天学习了JQuery的事件以及动画,感觉即将学习完JQuery,在回忆起上周学习的JavaScript,感觉好多刚刚学习的知识点都记得模 糊啦,这个是很让失望的,这里只说明了一点,课是听过啦, ...
- html 表单内容怎么获取不到,jquery中formdate一直获取不到对象中的[0]的值 包括本身也是一个空的数据怎么办?...
jquery中formdate一直获取不到对象中的[0]的值 包括本身也是一个空的数据怎么办? 再做一个前台的ajax方法 查网上用formdate方法上传.可是进了接口之后一直在控制台获取不到for ...
- jQuery中getJSON跨域原理详解
详见:http://blog.yemou.net/article/query/info/tytfjhfascvhzxcytp28 jQuery中getJSON跨域原理详解 前几天我再开发一个叫 河蟹工 ...
最新文章
- pytorch实现手写数字识别_Paddle和Pytorch实现MNIST手写数字集识别对比
- 数据挖掘提分三板斧!
- 4.边缘光照的描边shader
- linux下shell编程课程设计,Linux下shell编程实例
- css background 一半_CSS小技巧
- 自定义依赖注解无效_SpringValidation用注解代替代码参数校验解析
- Java方法的反射(对反射的进一步认识)
- firebase连接不上_如何在Firebase上托管Blazor应用程序
- c语言添加收支情况,C语言编写一个计算个人所得税的程序,要求输入收入金额,能够输...
- (转)PostGIS+QGIS+GeoServer+OpenLayers实现数据的存储、服务的发布以及地图的显示...
- Dev TreeList常用用法
- EfficientNet-B4-Ranger:自然复杂环境下温室黄瓜病害识别新方法(同时存在两种疾病)
- android 开发热更新技术Andfix 和 Tinker
- 9.看板方法---建立输入节奏
- Median of Two Sorted Array leetcode java
- 给linux默认mysql设置root密码
- \r,\n,\r\n的问题分析
- 捕获javaw的输出
- ioh3000t长虹盒子刷机固件 S905LB-高安-xian刷固件
- 电脑损坏,电脑文件损坏无法开机怎么办
热门文章
- RabbitMQ 入门系列(11)— RabbitMQ 常用的工作模式(simple模式、work模式、publish/subscribe模式、routing模式、topic模式)
- 【转载】写博意味着什么?
- 那还剩下多少学习激情?
- 【JavaScript总结】JavaScript语法基础:数据类型
- ?:在正则表达式中什么意思
- Qt实现 指针式时钟+动态时钟 (详细注释)
- 计算机视觉系列最新论文(附简介)
- Python七大原则,24种设计模式
- CSS Modules
- Ubuntu 系统安装OpenJDK 7,openjdk8