• jQuery.data的是jQuery的数据缓存系统


    jQuery.Data源码

    jQuery.data的是jQuery的数据缓存系统

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

    1 内部存储原理

    image

    这个原理很简单,原本要添加在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
    });
    复制代码
    image

    注意,一旦将 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);
    复制代码
    image

    ④[[value]]

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

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

    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);
    复制代码
    image

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

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

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

    b4722170-1292-4c0e-bb79-8665339ad935

    红色画框内的属性名就是由上面的语句生成的,指明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 ];

    这里获取的就是

    b5699b61-ea45-4a9a-b45c-ebd3727f28af

    如果能从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赋值(空对象)。

    image

    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的驼峰形式是否存在,后面的正则表达式用于去除驼峰形式前后的空格。

    15beac62-c2e7-453d-8cf6-a4814dbb9854

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

    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的数据对象。

    image

    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所有的属性;

    a7b4953a-c10f-4d0a-a507-6542fe126ee4

    这个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"]);
  • 相关阅读:
    新加坡金融科技节 | 蚂蚁金服CTO程立:面向全球开放,与合作伙伴共赢
    hadoop2.7.2集群搭建
    【Hive二】 Hive基本使用
    【Hive一】Hive安装及配置
    国内maven库链接地址,链接阿里的库,下载很快!!!
    【Divide and Conquer】169. Majority Element(easy)
    Unity3D制作粒子系统
    Unity3d制作游戏背包系统
    Unity3D射箭小游戏
    (eden)Delete character
  • 原文地址:https://www.cnblogs.com/Leo_wl/p/5747570.html
Copyright © 2020-2023  润新知