jQuery.Data源码

jQuery.data的是jQuery的数据缓存系统。它的主要作用就是为普通对象或者DOM元素添加数据。

1 内部存储原理

这个原理很简单,原本要添加在DOM元素本身的数据,现在被集中的存储在cache集合中。它们之间靠一个从1开始的数字键来联系着。这样DOM元素就不会像以前那么笨重了,更不会出现以前那种循环引用而引起的内存泄漏。现在DOM只需要保存好这个数字键值即可。这个属性值被保存在DOM元素的一个属性里,该属性名是由jQuery.expando生成的。

2 Data构造函数

Object.defineProperty( this.cache = {}, 0, {
    get: function() {
        return {};
    }
});

首先来看Object.defineProperty函数,它的作用为:将属性添加到对象,或修改现有属性的特性。那我们先来看下ECMAScript5中的属性。

2.1 ECMAScript5中的属性

ECMAScript5中有两种属性:数据属性和访问器属性。

2.1.1 数据属性:

数据属性包含一个数据值的位置,在这个位置可以读取和写入值。有4种特性用来限制其行为。

①[[configurable]]

能否通过delete删除属性,能否修改属性的特性,能否把属性改为访问器属性。默认为true。

var  obj={name:"abc"};
Object.defineProperty(obj,"name",{
    configurable:false
});
console.log(obj.name);
delete obj.name;
console.log(obj.name);
Object.defineProperty(obj,"name",{
    configurable:true
});

注意,一旦将 configurable 设为false,就再也设置不回去了。而且还会报错。

②[[enumerable]]

在for-in循环是否能获取到属性。默认为true。ECMAScript规定,由程序员定义的属性的该特性都为true。

③[[writable]]

能否修改属性的值。默认为true。

var  obj={name:"abc"};
Object.defineProperty(obj,"name",{
    writable:false
});
console.log(obj.name);
obj.name="111";
console.log(obj.name);

④[[value]]

属性值所在的地方。读取属性值的时候获取的就是它,写属性值的时候也是往这里写。

var  obj={name:"abc"};
console.log(obj.name);
Object.defineProperty(obj,"name",{
    value:111
});
console.log(obj.name);

2.1.2访问器属性

访问器属性不包含实际的属性值,它包含两个函数(getter和setter)

①[[get]]

读取属性时调用的函数。默认为undefined

②[[set]]

写入属性时调用的函数。默认为undefined

var obj={};
Object.defineProperties(obj,{
    name:{
        get:function () {
            return obj["_name"];
        },
        set:function (name) {
            if(name != "C#"){
                obj["_name"]=name;
            }
        }
    },
    label:{
        get:function () {
            return "你不能改变我!!!";
        }
    }
});
obj.name="JavaScript";
console.log(obj.name);
obj.label="我偏要改变你!!!";
console.log(obj.label);

上面给cache的属性0,只给了get属性。所以不能为该属性赋值。只能获取。

this.expando = jQuery.expando + Math.random();

这个expando就是用来为DOM元素或者对象存储在cache中的键的。即它将作为DOM元素的一个属性被存储这。

红色画框内的属性名就是由上面的语句生成的,指明div1的属性存储在cache的1属性中。

Data.uid = 1;

该属性表示cache的属性将从1开始自增。因为0已经被这个冻结的空JSON占用了,所以从1开始。我们下次再为某DOM元素添加属性时,它将被保存在cache的2属性中。

3 允许的添加属性的元素

Data.accepts = function( owner ) {
    return owner.nodeType ?
        owner.nodeType === 1 || owner.nodeType === 9 : true;
};

这段代码写的非常干练。表示如果owner是DOM元素则只有ELEMENT_NODE和DOCUMENT_NODE两种元素能添加属性,如果owner是一个对象,则都可以添加属性。

4 原型属性

4.1 key: function( owner ) {

if ( !Data.accepts( owner ) ) {
    return 0;
}

如果owner不能被添加data,则返回cache的第0个元素。

unlock = owner[ this.expando ];

这里获取的就是

如果能从owner中找到这个属性,则说明以前为它添加过值,也就是说,它已经拥有了一个在cache中的key。

if ( !unlock ) {
    unlock = Data.uid++;
    try {
        descriptor[ this.expando ] = { value: unlock};
        Object.defineProperties( owner, descriptor );
    } catch ( e ) {
        descriptor[ this.expando ] = unlock;
        jQuery.extend( owner, descriptor );
    }
}

如果找不到,则说明是第一次为owner添加属性,则要创建一个key。这个key就是在Data的uid的基础上加1.Data的uid是一个静态属性。这样就能记录前一个元素的key是多少,这次的key又应该是多少。try块里面为owner(即DOM元素或者对象)添加它在cache中的索引。这里会出现兼容性的问题。在Android系统<4时会出现安全问题,因此jQuery使用extend静态方法将descriptor扩展到owner上面。

if ( !this.cache[ unlock ] ) {
    this.cache[ unlock ] = {};
}

这里为owner对应在cache中的key赋值一个Object。

这个的key方法的作用就是为DOM元素创建属性,为cache中对应的key赋值(空对象)。

key返回的是这个unlock,即owner对象在cache中对应的key。

4.2 set: function( owner, data, value ) {

unlock = this.key( owner ),
cache = this.cache[ unlock ];

第一句不用解释,第二句或者cachekey值。以上图所示,则这里的cache目前还是{},因为在此之前还没有为div1添加过属性。

if ( typeof data === "string" ) {
    cache[ data ] = value;

我们下面的代码走的就是这里:

$("#div1").data("name","div1");

但是我们有很多属性要设置的时候,

$("#div1").data({name:"div1",from:"0",to:"100"});

jQuery的处理方式是这样的:

// Handle: [ owner, { properties } ] args
} else {
    // Fresh assignments by object are shallow copied
    if ( 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 ];
        }
    }
}

其实这里个人以为不用再判断了,因为extend里面也是用for循环将data的属性扩展到cache中的。

4.3 get: function( owner, key ) {

var cache = this.cache[ this.key( owner ) ];
return key === undefined ? cache : cache[ key ];

获取属性,代码非常简单。如果key不存在则返回cache对象。

4.4 access: function( owner, key, value ) {

这里对get和set方法的统一访问。

4.5 remove: function( owner, key ) {

unlock = this.key( owner ),
cache = this.cache[ unlock ];

获取owner的在cache中的数据对象,this.key返回的是owner在cache中的key。

if ( key === undefined ) {
    this.cache[ unlock ] = {};

如果不指定要删除那个属性的话,jQuery会删除owner所有的数据属性。

否则再判断key是不是数组,

if ( jQuery.isArray( key ) ) {
    name = key.concat( key.map( jQuery.camelCase ) );
}

是数组的话,将key数组和用key的每一项转驼峰后的数组合并,即:

$("#div1").remove(["one_key","two_key"]);

经过上面的代码,key为变为["one_key","two_key","oneKey","twoKey"]。后面会将这4个属性都删除掉。

那如果key不是数组:

camel = jQuery.camelCase( key );
if ( key in cache ) {
    name = [ key, camel ];
} else {
    name = camel;
    name = name in cache ?
        [ name ] : ( name.match( core_rnotwhite ) || [] );
}

同样先输转驼峰,然后判断key是否存在,不存在则判断key的驼峰形式是否存在,后面的正则表达式用于去除驼峰形式前后的空格。

当这些情况过滤完之后,进行删除操作:

i = name.length;
while ( i-- ) {
    delete cache[ name[ i ] ];
}

在while循环里面使用delete进行删除。

4.6 hasData: function( owner ) {

cache中是否拥有woner的数据对象。

4.7 discard: function( owner ) {

删除cache中的woner的数据对象。

5 创建两个私有的cache

data_user = new Data();
data_priv = new Data();

所以,我们在jQuery的外面不能直接拿到这个cache。因为它是jQuery的局部变量。data_user供开发人员使用,data_priv供jQuery内部使用。

6 创建对外接口(工具方法和原型方法)

由于Data构造器是jQuery私有的,我们在外面不能访问到,所以前面的那些方法,我们也不能直接访问,jQuery在这里,给我们提供了一些接口。来操作data_user,为DOM元素和Object进行属性操作。工具方法非常简单只是对data_user方法的封装而已。我们主要看下原型方法。

jQuery.fn.extend({
    data: function( key, value ) {

设值和取值都会进入上面的方法。

这里有这样一个思想,如果是设值的时候,则给选集中所有的选项设值,如果获取值的时候,只获取第一个选项的值。

if ( key === undefined ) {
    if ( this.length ) {
        data = data_user.get( elem );
        if ( elem.nodeType === 1 && !data_priv.get( elem, "hasDataAttrs" ) ) {
            attrs = elem.attributes;
            for ( ; i < attrs.length; i++ ) {
                name = attrs[ i ].name;
                if ( name.indexOf( "data-" ) === 0 ) {
                    name = jQuery.camelCase( name.slice(5) );
                    dataAttr( elem, name, data[ name ] );
                }
            }
            data_priv.set( elem, "hasDataAttrs", true );
        }
    }
    return data;
}

在第3行,已经取到cache中elem对应的属性了,下面jQuery由将elem的attributes里的所有属性添加到elem的    。

hasDataAttrs属性是我们自己添加,因为下面这段代码只需要执行一次即可。代码第6行,获取elem所有的属性;

这个NamedNodeMap类型,我们平时很少直接用它,它和NodeList,HTMLCollection一样都是“动态”的。NamedNodeMap集合中的每一项都是Attr类型,Attr对象有3个属性:name,value和specified。

在下面的for循环里面,就检测这个name属性中是否含有"data-"前缀。有的话就去掉它,并且将剩余部分转为驼峰形式。

如果key不存在,则表示获取选集中第一个选项的所有属性值。elem表示第一个选项。

if ( typeof key === "object" ) {
    return this.each(function() {
        data_user.set( this, key );
    });
}

对应这种形式:

$("#div1").remove(["one_key","two_key"]);
时间: 2024-10-10 17:45:16

jQuery.Data源码的相关文章

jQuery.access源码分析

基本理解 jQuery.attr是jQuery.attr,jQuery.prop,jQuery.css提供底层支持,jQuery里一个比较有特色的地方就是函数的重载, 比如attr,有如下几种重载 $('#box').attr('title') $('#box').attr('title','标题') $('#box').attr({title:'标题',data-menu-toggle:'dropdown'}) $('#box').attr('title',function () {....}

jQuery方法源码解析--jQuery($)方法(一)

jQuery方法源码解析--jQuery($)方法 注: 1.本文分析的代码为jQuery.1.11.1版本,在官网上下载未压缩版即可 2.转载请注明出处 jQuery方法: 这个方法大家都不陌生,在使用过程中,它还有另外一个名字,美元符号:$,$(...)其实就是jQuery(...); 它有很多种用法,通常都返回一个jquery对象,也可以作为$(document).ready(...);的简写形式,分析之前先看一下jQuery都有什么用法. 1.jQuery( selector [, co

jQuery选择器源码分析和easyui核心分析

写在选择器源码分析之前 这里指对1.7.2版本的源码分析,更高版本添加了更多代码. 整个jQuery的代码是写在一个(function(window, undefined){})(window);这样一个闭包里.请思考,为什么要这样做? 将其写在一个闭包函数里,并传入window直接运行的好处有三: 1,统一命名空间,防止变量的污染:  2,将window作为参数传入函数,在函数里调用window的时候,就不用再去找外层的对象,可以提高效率 : 3,undefined并不是javascript的

【深入浅出jQuery】源码浅析2--奇技淫巧

最近一直在研读 jQuery 源码,初看源码一头雾水毫无头绪,真正静下心来细看写的真是精妙,让你感叹代码之美. 其结构明晰,高内聚.低耦合,兼具优秀的性能与便利的扩展性,在浏览器的兼容性(功能缺陷.渐进增强)优雅的处理能力以及 Ajax 等方面周到而强大的定制功能无不令人惊叹. 另外,阅读源码让我接触到了大量底层的知识.对原生JS .框架设计.代码优化有了全新的认识,接下来将会写一系列关于 jQuery 解析的文章. 我在 github 上关于 jQuery 源码的全文注解,感兴趣的可以围观一下

jQuery.attributes源码分析(attr/prop/val/class)

回顾 有了之前的几篇对于jQuery.attributes相关的研究,是时候分析jQuery.attr的源码了 Javascript中的attribute和property分析 attribute和property兼容性分析 jQuery.access源码分析 结构 jQuery.fn.extend({ attr: function (name, value) { }, removeAttr: function (name) { }, prop: function (name, value) {

自写图片遮罩层放大功能jquery插件源码,photobox.js 1.0版,不兼容IE6

阿嚏~~~ 话说本屌丝没啥开发插件的经验,但是天公不作美,公司需要让我自己开发个图片放大的插件 但公司老大的话,犹如吾皇之圣旨,微臣必当肝脑涂地,莫敢不从啊~~~ 于是乎,作为一个超级小白,本人只能瞎研究了,幸好黑天不负屌丝人,本屌丝终于搞出来了,虽然不尽善尽美,但是功能还是可以用的啦 先附上源码,求各种大神指导: /******************************* * photobox跨浏览器兼容插件 v1.0(不支持IE6) * 格式:<a href="big.jpg&q

模拟jQuery底层源码的链式调用和常用的$()方法的实现

最近在看jQuery框架的源码,感觉还是学到不少东西的,所以就想总结一下自己的知识,和广大的前端爱好者一起 交流一下,我下面所说的并不是直接对jQuery的源码来解读,我是模拟一下jQuery底层源码的链式调用大概是怎么 实现的和常用的$功能是怎么实现的.好了废话不多说了.你要看这个,你就要对jQuery有一定的了解,最起码你要用过jQuery.首先看下jQuery的源码开始是怎么写的 (function( window, undefined){    })(window);它的代码就是被这个块

jQuery.data() 与 jQuery(elem).data()源码解读

之前一直以为 jQuery(elem).data()是在内部调用了 jQuery.data(),看了代码后发现不是.但是这两个还是需要放在一起看,因为它们内部都使用了jQuery的数据缓存机制.好吧,重点就是data_user对象,跟另一个data_priv是姐妹~~ 先来看下jQuery.data(): 它调用了data_user.access( elem, name, data ); 那么data_user到底是什么鬼呢?? 我们看到,它是由Data构造的实例,Data构造器都做了一些什么呢

《锋利的jquery》源码整理——jquery技巧上

        在看<锋利的jquery>这本书的时候,书末尾总结了jquery的一些用法技巧,感觉很实用,先收藏着以后用到,可以借鉴看看.   一,资源(在w3cfuns资源中可以找到第一版和第二版)          <锋利的jquery>: http://pan.baidu.com/share/link?shareid=1725756399&uk=4245516461(PDF)                                        http:/