jQuery 3.0 在6月9日正式发布了,3.0 也被称为下一代的 jQuery 。这个版本从14年10月开始,其中发布过一次beta 版(2016/1/14,)和候选版(2016/05/20)。一路走来,颇为不易。

文章目录

  1. Data浅析
  2. Data在jQuery内部的使用
  3. 1.x.x 和 2.x.x 的比较

一、Data浅析

jQuery 3.0 中的 Data 是内部使用的,定义为一个“类”。一共用它创建了两个对象,dataPriv 和 dataUser。Data 有 1 个对象属性(expando)和类属性(uid),有 6 个方法,如下

下面分别解读

1、Data.uid

这是一个从 1 开始用来自增的数字。

2、expando

由 jQuery.expando 和 uid 组合而成,它用来作为元素(如DOM元素)的key,是唯一的。jQuery.expando 的生成如下

jQuery.expando = "jQuery" + ( version + Math.random() ).replace( /\D/g, "" )

即 'jQuery' + (版本号 + 随机数),然后把非数字的都去掉,比如

"jQuery" + "3.0.0" + 0.129896303388626 == "jQuery3.0.00.129896303388626"

去掉非数字变为

"jQuery30009423638425146147"

  

jQuery 3.0 内部变量 dataPriv 和 dataUser 生成 expando 如下

jQuery 300 024727210109188635 1
jQuery 300 024727210109188635 2

第三部分是随机数,每次刷新都会变,其它部分的不变。

3、cache

cache 方法会给 owner 上绑定一个对象作为存储,owner 必须满足 acceptData 的,cache 会以 this.expando 为线索 key。
owner 有两种,一中是DOM元素(nodeType为1和9),另一种则是普通的JS对象。诸如 文本节点(nodeType=3)、注释节点(nodeType=8) 一律不添加。

acceptData 的定义如下

var acceptData = function( owner ) {

	// Accepts only:
// - Node
// - Node.ELEMENT_NODE
// - Node.DOCUMENT_NODE
// - Object
// - Any
/* jshint -W018 */
return owner.nodeType === 1 || owner.nodeType === 9 || !( +owner.nodeType );
};

acceptData 在 3.0 中一共有 3 处使用,分别为

  1. Data 类的 cache 方法,为私有方法不提供给程序员使用。
  2. $.cleanData 方法,清空元素关联的所有缓存数据。为公开方法,但很少使用。该方法被用在 empty、html、replaceWith、remove 方法中。
  3. $().trigger 方法,主动派发事件,为公开方法。

如果是 DOM 元素,则直接使用点操作符赋值,如果是普通 JS 对象则使用 ES5 的 Object.defineProperty 方法,这也是 jQuery 3.0 会使用新 API 的体现。

// If it is a node unlikely to be stringify-ed or looped over
// use plain assignment
if ( owner.nodeType ) {
owner[ this.expando ] = value; // Otherwise secure it in a non-enumerable property
// configurable must be true to allow the property to be
// deleted when data is removed
} else {
Object.defineProperty( owner, this.expando, {
value: value,
configurable: true
} );
}

转换成如下代码

elem['jQuery3000247272101091886351'] = dataObj;

var person = {name: 'John', age: 30};
Object.defineProperty( person, 'jQuery3000247272101091886351', {
value: dataObj,
configurable: true
} );

cache 方法就是这样,传入 owner,只有第一次会 set ,返回 value (一个空对象),之后取到 value 后直接返回。

源码

cache: function( owner ) {

	// Check if the owner object already has a cache
var value = owner[ this.expando ]; // If not, create one
if ( !value ) {
value = {}; // We can accept data for non-element nodes in modern browsers,
// but we should not, see #8335.
// Always return an empty object.
if ( acceptData( owner ) ) { // If it is a node unlikely to be stringify-ed or looped over
// use plain assignment
if ( owner.nodeType ) {
owner[ this.expando ] = value; // Otherwise secure it in a non-enumerable property
// configurable must be true to allow the property to be
// deleted when data is removed
} else {
Object.defineProperty( owner, this.expando, {
value: value,
configurable: true
} );
}
}
} return value;
},

4、set

上面的 cache 方法为 owner 建立一个以 expando 为 key 的空对象,后面所有的方法都围绕这个空对象来展开,这个空对象就被称为缓存对象,后面所有的数据都添加到它上面。set 就是给这个对象来添砖加瓦,set 每次都是先取回 cache ,再给其添加新的属性及数据。如果 data 是字符串,则以它为 key 添加,如果是对象,则遍历它添加。只需注意一点,横线连接符内部会被转成驼峰格式,这也是为了对 H5 data-xxx 的兼容 。

源码

set: function( owner, data, value ) {
var prop,
cache = this.cache( owner ); // Handle: [ owner, key, value ] args
// Always use camelCase key (gh-2257)
if ( typeof data === "string" ) {
cache[ jQuery.camelCase( data ) ] = value; // Handle: [ owner, { properties } ] args
} else { // Copy the properties one-by-one to the cache object
for ( prop in data ) {
cache[ jQuery.camelCase( prop ) ] = data[ prop ];
}
}
return cache;
},

  

5、get

get 简单至极,传 key 则从 cache 上取回该 key 的值,无则取回整个 cache。

源码

get: function( owner, key ) {
return key === undefined ?
this.cache( owner ) : // Always use camelCase key (gh-2257)
owner[ this.expando ] && owner[ this.expando ][ jQuery.camelCase( key ) ];
},

  

6、access

这个方法即时 getter,也是 setter,如此而已。它存在的原因是由于 jQuery 的 API 有很多是身兼 setter 和 getter 的功能,比如 .data、.html、.text。有了 access 可以少写很多 if / else。

getter 条件

  1. key 是 undefined,这时取整个 cache
  2. key 是字符串且value 是undefined,这是取指定 key 的值

setter 条件

  1. owner、key、value 这三个参数都传

源码

access: function( owner, key, value ) {

	// In cases where either:
//
// 1. No key was specified
// 2. A string key was specified, but no value provided
//
// 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 && typeof key === "string" ) && value === undefined ) ) { return this.get( owner, key );
} // When the key is not a string, or both a key and value
// are specified, set or extend (existing objects) with either:
//
// 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;
},

7、remove

清空绑定元素(owner)上面的缓存对象,依然需要先通过 this.expando 拿到 cache,如果传了 key 则删除指定 key 的值(key自身也被删除)。
当然 jQuery API 保持已有的方便性,key 可以为一个数组,这样可以批量删除多个 key。如果 key 没传则将整个 cache 删除,这里区分了 DOM 和普通 JS 对象,DOM 对象使用undefined赋值,JS 对象则使用 delete。

源码

remove: function( owner, key ) {
var i,
cache = owner[ this.expando ]; if ( cache === undefined ) {
return;
} if ( key !== undefined ) { // Support array or space separated string of keys
if ( jQuery.isArray( key ) ) { // If key is an array of keys...
// We always set camelCase keys, so remove that.
key = key.map( jQuery.camelCase );
} else {
key = jQuery.camelCase( key ); // If a key with the spaces exists, use it.
// Otherwise, create an array by matching non-whitespace
key = key in cache ?
[ key ] :
( key.match( rnotwhite ) || [] );
} i = key.length; while ( i-- ) {
delete cache[ key[ i ] ];
}
} // Remove the expando if there's no more data
if ( key === undefined || jQuery.isEmptyObject( cache ) ) { // Support: Chrome <=35 - 45
// Webkit & Blink performance suffers when deleting properties
// from DOM nodes, so set to undefined instead
// https://bugs.chromium.org/p/chromium/issues/detail?id=378607 (bug restricted)
if ( owner.nodeType ) {
owner[ this.expando ] = undefined;
} else {
delete owner[ this.expando ];
}
}
}, 

8、hasData

用来判断 owner 上是否有缓存数据,返回 true 或 false。

源码

hasData: function( owner ) {
var cache = owner[ this.expando ];
return cache !== undefined && !jQuery.isEmptyObject( cache );
}

二、Data在jQuery内部的使用

以上解读完了 Data 的所有方法,上面也提到 Data 类是在 jQuery 内部使用的,一共创建了它的两个对象:dataPriv 和 dataUser。

这两个对象在 3.0.0 中有着明确的分工,dataPriv 可以猜测到是 “data” 和 “private” 两个单词的组合后简写。即 dataPriv 是私有的用来服务 jQuery 内部方法,dataUser 用来服务那些公开给用户使用的方法。

下面看下这两个对象分布在哪些模块中使用。

完整版点击展开可查看

dataPriv

	公共
$.hasData
$.cleanData
cloneCopyEvent 队列
$().queue
$()._queueHooks
$().promise 动画
$().animate
$().stop
$().finish
showHide 事件
$.event.add
$.event.remove
$.event.dispatch
$.event.trigger 其它
setGlobalEval
domManip
defaultPrefilter
$().toggleClass dataUser 公共
$.hasData
$.cleanData
cloneCopyEvent 数据缓存
$.data
$.removeData
$().data
$().removeData 其它
dataAttr

以上可以看到,除了“公共”,DataPriv 用在了 jQuery 的 队列、动画、事件等模块;dataUser 用在了数据缓存及 dataAttr 模块。

“公共” 是指这三个方法内都用到了 dataPriv 和 dataUser

$.hasData(elem)

用来判断 elem 上是否绑定了相关的数据缓存,返回 true 和false,只有 dataPriv 和 dataUser 上都没有才返回 false

源码

hasData: function( elem ) {
return dataUser.hasData( elem ) || dataPriv.hasData( elem );
},

$.cleanData(elems)

清空 elem 上绑定的所有数据缓存,理所当然的需要同时清空 dataPriv 和 dataUser 上的。
注意:虽然这个方法在公开暴露在了 $ 上, 但官网API上却没有该方法的介绍。另使用不当会造成严重后果,比如绑定了事件后(.on),调用该方法,绑定的事件将全部失效。因为会清空 dataPriv 内的所有数据。

cloneCopyEvent(src, dest)

这是一个内部方法,$.clone 会使用到它。克隆元素时除了会克隆 node 节点外,绑定在 node 上的数据也会被克隆过去。比如

var cloneNode = $.clone(elem);

把 elem 克隆给 cloneNode,此时 elem 上添加的事件 cloneNode 上也会有。

三、1.x.x 和 2.x.x 的比较

jQuery 1.x 系列 和 2.x 系列的版本对 数据缓存模块 的实现差异还是很大的。大家可以对比我11年的这篇文章

1. 缓存的数据结构

1.x (直到1.11.2) 缓存都是存储在 jQuery.cache 上的,2.x(包括3.x) 则使用了一个内部类 Data 做缓存,其主要用到了两个对象 dataPriv 和 dataUser。很明显 2.x 做的更好,它所有的缓存数据都是私有的,不会存在被误写的风险,而 1.x 的 jQuery.cache 是公开的,如果被误写(比如某个同学想当然的给$上添加一个cache对象)后果不堪设想。

2. jQuery._data

看到这个下划线就知道是私有的(约定式),在 1.x 中是仅在内部使用的,不提供给开发者。以 1.11.2 示例、这个方法被事件模块、队列模块、动画模块、setGlobalEval、cloneCopyEvent、fixCloneNodeIssues、domManip、showHide、defaultPrefilter、toggleClass 使用。3.x 则使用 dataPriv 和 dataUser 替代,大家可以对比看看。

(2/3).x 相比 1.x 明显更优,dataPriv 和 dataUser 是真正的私有的(封装的更好,更安全),比起 1.x 约定式的私有 jQuery._data。虽然 3.0.0 还保守的兼容了 jQuery._data,相信过不了多久的后续版本就会剔除。

3. 重构

1.x 以 $._data 为中心,以它来辅助实现其它 API, (2/3).x 以 dataPriv/dataUser 为中心来实现。(2/3).x 将代码重构后提取出了 Data 类,更加清晰。

相关:

http://naotu.baidu.com/file/c287195ae96011f7511571a4280042c7?token=ddb7c115786ff90f

http://naotu.baidu.com/file/186bfe75ebe2878fa4d70856f0f33672?token=c8b112b939e0f65c

http://www.cnblogs.com/snandy/archive/2011/06/10/2077298.html

http://www.cnblogs.com/snandy/p/4650599.html

jQuery 3.0 的 Data 浅析的更多相关文章

  1. jQuery 3.0 的 Data

    jQuery 3.0 的 Data Snandy If you cannot hear the sound of the genuine in you, you will all of your li ...

  2. jQuery 3.0的domManip浅析

    domManip 这个函数的历史由来已久,从 jQuery 1.0 版本开始便存在了,一直到最新的 jQuery 版本.可谓是元老级工具函数. domManip 的主要功能是为了实现 DOM 的插入和 ...

  3. jQuery 2.0.3 源码分析core - 选择器

         声明:本文为原创文章,如需转载,请注明来源并保留原文链接Aaron,谢谢!      打开jQuery源码,一眼看去到处都充斥着正则表达式,jQuery框架的基础就是查询了,查询文档元素对象 ...

  4. jQuery 2.0.3 源码分析 事件体系结构

    那么jQuery事件处理机制能帮我们处理那些问题? 毋容置疑首先要解决浏览器事件兼容问题 可以在一个事件类型上添加多个事件处理函数,可以一次添加多个事件类型的事件处理函数 提供了常用事件的便捷方法 支 ...

  5. jQuery 3.0 的变化

    时隔 3 个月,jQuery 团队终于发布了 3.0 Alpha 版本.有两个版本 jQuery compat 3.0 和 jQuery 3.0. jQuery compat 3.0 对应之前的 1. ...

  6. jquery ajax自定义分页组件(jquery.loehpagerv1.0)原创

    简单的两个步骤截可调用 <script src="<%=basePath%>/resources/js/jquery-1.7.1.min.js"></ ...

  7. jQuery中的each, data, 插件

    一.  each() $(' ').each(function (){...}) jQuery.each(collection, callback(indexInArray, valueOfEleme ...

  8. jQuery 3.0正式发布

    jQuery 基金会刚刚发布了该 JavaScript 框架的 3.0 版本,并且首次抛弃了对老旧的 IE 浏览器的支持.jQuery 3.0 的工作始于 2014 年 10 月,其最初目标是在 2. ...

  9. jQuery 2.0发布,不再支持IE6/7/8

    有时发现jQuery库引用的都对,javascript代码写的也没问题,可是jquery就是出现问题,额--我发现换个jquery库就没问题了,长时间不关注jquery的问题而已: 很多人都没有使用最 ...

随机推荐

  1. 无聊的人用JS实现了一个简单的打地鼠游戏

    直入正题,用JS实现一个简单的打地鼠游戏 因为功能比较简单就直接裸奔JS了,先看看效果图,或者 在线玩玩 吧 如果点击颜色比较深的那个(俗称坏老鼠),将扣分50:如果点击颜色比较浅的那个(俗称好老鼠) ...

  2. asp.net MVC 回顾 Html.ActionLink

    在asp.net MVc中想生成一个超链接有很多种方式,通过直接输入<a>.Html.ActionLink.Html.RouteLink等等,今天我们要阐述的就是Html.ActionLi ...

  3. 2.简单的Code First例子(EF Code-First系列)

    现在假想,我们想要为讴歌学校创建一个应用程序,这个程序需要能够来添加或者更新学生,分数,教师还有课程信息. 代替之前我们的做法:先是创建数据库,现在我们不这么做,我们先来创建领域类,首先我来创建两个简 ...

  4. 5.Inheritance Strategy(继承策略)【EFcode-first系列】

    我们已经在code-first 约定一文中,已经知道了Code-First为每一个具体的类,创建数据表. 但是你可以自己利用继承设计领域类,面向对象的技术包含“has a”和“is a”的关系即,有什 ...

  5. Asp.net mvc返回Xml结果,扩展Controller实现XmlResult以返回XML格式数据

    我们都知道Asp.net MVC自带的Action可以有多种类型,比如ActionResult,ContentResult,JsonResult……,但是很遗憾没有支持直接返回XML的XmlResul ...

  6. 原生JS投票特效

    效果:http://hovertree.com/texiao/js/24/ 效果图: 代码如下: <!DOCTYPE html> <html lang="en"& ...

  7. 使用SwipeListView实现滑动效果

    QQ的滑动删除效果很不错,要实现这种效果,可以使用SwipeListView.1. 下载com.fortysevendeg.swipelistview这个项目(以前GitHub上有,现在GitHub上 ...

  8. UVALive 6911---Double Swords(贪心+树状数组(或集合))

    题目链接 https://icpcarchive.ecs.baylor.edu/index.php?option=com_onlinejudge&Itemid=8&page=show_ ...

  9. E - The Values You Can Make

    E - The Values You Can Make Description Pari wants to buy an expensive chocolate from Arya. She has ...

  10. json-lib的使用《二》

    上篇文章主要集中在了使用json-lib来实现JSON字符串和java中的对象的互转上,忽视了json-lib本身的功能,json-lib中有两个类比较重要:JSONObject和JSONArray, ...