热门标签 | HotTags
当前位置:  开发笔记 > 前端 > 正文

浅析jQuery3.0中的Data

jQuery3.0在6月9日正式发布了,3.0也被称为下一代的jQuery。这篇文章主要介绍了jQuery3.0中的Data,包括Data在jQuery内部的使用和1.x.x和2.x.x的比较的相关内容,需要的朋友可以参考下

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

一、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" + ".." + . == "jQuery..." 

去掉非数字变为

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 处使用,分别为

Data 类的 cache 方法,为私有方法不提供给程序员使用。$.cleanData 方法,清空元素关联的所有缓存数据。为公开方法,但很少使用。该方法被用在 empty、html、replaceWith、remove 方法中。$().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,如此而已。

getter 条件

key 是 undefined,这时取整个 cachekey 是字符串且value 是undefined,这是取指定 key 的值

setter 条件

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&#63;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 类,更加清晰。

以上所述是小编给大家介绍的jQuery 3.0中的Data的全部叙述,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对网站的支持!


推荐阅读
  • 使用正则表达式爬取36Kr网站首页新闻的操作步骤和代码示例
    本文介绍了使用正则表达式来爬取36Kr网站首页所有新闻的操作步骤和代码示例。通过访问网站、查找关键词、编写代码等步骤,可以获取到网站首页的新闻数据。代码示例使用Python编写,并使用正则表达式来提取所需的数据。详细的操作步骤和代码示例可以参考本文内容。 ... [详细]
  • 如何压缩网站页面以减少页面加载时间
    本文介绍了影响网站打开时间的两个因素,即网页加载速度和网站页面大小。重点讲解了如何通过压缩网站页面来减少页面加载时间。具体包括图片压缩、Javascript压缩、CSS压缩和HTML压缩等方法,并推荐了相应的压缩工具。此外,还提到了一款Google Chrome插件——网页加载速度分析工具Speed Tracer。 ... [详细]
  • css元素可拖动,如何使用CSS禁止元素拖拽?
    一、用户行为三剑客以下3个CSS属性:user-select属性可以设置是否允许用户选择页面中的图文内容;user-modify属性可以设置是否允许输入 ... [详细]
  • 这是原文链接:sendingformdata许多情况下,我们使用表单发送数据到服务器。服务器处理数据并返回响应给用户。这看起来很简单,但是 ... [详细]
  • Python瓦片图下载、合并、绘图、标记的代码示例
    本文提供了Python瓦片图下载、合并、绘图、标记的代码示例,包括下载代码、多线程下载、图像处理等功能。通过参考geoserver,使用PIL、cv2、numpy、gdal、osr等库实现了瓦片图的下载、合并、绘图和标记功能。代码示例详细介绍了各个功能的实现方法,供读者参考使用。 ... [详细]
  • HTML学习02 图像标签的使用和属性
    本文介绍了HTML中图像标签的使用和属性,包括定义图像、定义图像地图、使用源属性和替换文本属性。同时提供了相关实例和注意事项,帮助读者更好地理解和应用图像标签。 ... [详细]
  • 本文介绍了南邮ctf-web的writeup,包括签到题和md5 collision。在CTF比赛和渗透测试中,可以通过查看源代码、代码注释、页面隐藏元素、超链接和HTTP响应头部来寻找flag或提示信息。利用PHP弱类型,可以发现md5('QNKCDZO')='0e830400451993494058024219903391'和md5('240610708')='0e462097431906509019562988736854'。 ... [详细]
  • 本文介绍了响应式页面的概念和实现方式,包括针对不同终端制作特定页面和制作一个页面适应不同终端的显示。分析了两种实现方式的优缺点,提出了选择方案的建议。同时,对于响应式页面的需求和背景进行了讨论,解释了为什么需要响应式页面。 ... [详细]
  • GPT-3发布,动动手指就能自动生成代码的神器来了!
    近日,OpenAI发布了最新的NLP模型GPT-3,该模型在GitHub趋势榜上名列前茅。GPT-3使用的数据集容量达到45TB,参数个数高达1750亿,训练好的模型需要700G的硬盘空间来存储。一位开发者根据GPT-3模型上线了一个名为debuid的网站,用户只需用英语描述需求,前端代码就能自动生成。这个神奇的功能让许多程序员感到惊讶。去年,OpenAI在与世界冠军OG战队的表演赛中展示了他们的强化学习模型,在限定条件下以2:0完胜人类冠军。 ... [详细]
  • Java和JavaScript是什么关系?java跟javaScript都是编程语言,只是java跟javaScript没有什么太大关系,一个是脚本语言(前端语言),一个是面向对象 ... [详细]
  • 本文介绍了网页播放视频的三种实现方式,分别是使用html5的video标签、使用flash来播放以及使用object标签。其中,推荐使用html5的video标签来简单播放视频,但有些老的浏览器不支持html5。另外,还可以使用flash来播放视频,需要使用object标签。 ... [详细]
  • 本文由编程笔记#小编为大家整理,主要介绍了markdown[软件代理设置]相关的知识,希望对你有一定的参考价值。 ... [详细]
  • CSS|网格-行-结束属性原文:https://www.gee ... [详细]
  • FIN7后门工具伪装成白帽工具进行传播
    fin7,后门,工具,伪装,成,白, ... [详细]
  • 本文由编程笔记#小编为大家整理,主要介绍了css回到顶部按钮相关的知识,希望对你有一定的参考价值。 ... [详细]
author-avatar
zhihong520珠珠_448
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有