jQuery源码学习(三)

功能检测(support)

jQuery解决了不同浏览器之间的兼容性问题,首先通过support检测出都存在哪些兼容性问题。

数据缓存data()

数据缓存,避免大数据量的元素挂载、预防内存泄露

attr()、prop()和data()

attr()
<body>
<div id="box"></div>
<script type="text/javascript" src="../../jquery-2.1.4.js"></script>
<script type="text/javascript">
$(function(){
    $("#box").attr('name','hello');
    alert($('#box').attr('name'));//hello
});
</script>
</body>

相应的dom方法

//attr就是基于setAttribute和getAttribute来实现的
    var oBox = document.getElementById("box");
    oBox.setAttribute('name','hello');
    alert(oBox.getAttribute('name'));
prop()
<div id="box"></div>
<script type="text/javascript" src="../../jquery-2.1.4.js"></script>
<script type="text/javascript">
$(function(){
    $("#box").prop('name','hello');
    alert($('#box').prop('name'));//hello
});
</script>
</body>

相应的dom方法

document.getElementById("box")['name'] = 'hello';
alert(document.getElementById("box")['name']);
//或者使用点语法
document.getElementById("box").name = 'hello';
alert(document.getElementById("box").name);
data()
<body>
<div id="box"></div>
<script type="text/javascript" src="../../jquery-2.1.4.js"></script>
<script type="text/javascript">
$(function(){
    //attr和prop比较适合设置单个属性
    //data方法适合设置json等多个数据属性
    $("#box").data('name','hello');
    alert($('#box').data('name'));//hello
});
</script>
</body>

内存泄露

内存泄露是指一块被分配的内存既不能使用,又不能回收,直到浏览器进程结束。在C++中,因为是手动管理内存,内存泄露是经常出现的事情。而现在流行的C#和Java等语言采用了自动垃圾回收方法管理内存,正常使用的情况下几乎不会发生内存泄露。浏览器中也是采用自动垃圾回收方法管理内存,但由于浏览器垃圾回收方法有bug,会产生内存泄露。

常见内存泄露的几种情况
  • 循环引用
  • Javascript闭包
  • DOM插入

DOM元素与对象之间互相引用,大部分浏览器就会出现内存泄露
js对于内存的管理采用了垃圾回收机制
Demo:

<body>
<div id="box"></div>
<script type="text/javascript" src="../../jquery-2.1.4.js"></script>
<script type="text/javascript">
var oDiv = document.getElementById("box");
var obj = {};
//DOM元素引用了对象,对象引用了DOM元素
//这样就会产生内存泄漏
oDiv.name = obj;
obj.age = oDiv;
</script>
</body>

这个DOM元素的引用将不会在脚本停止的时候被垃圾回收器回收。要想破坏循环引用,引用DOM元素的对象或DOM对象的引用需要被赋值为null。
而在jQuery中使用data给对象挂载属性,可以防止内存泄露,那么jQuery是如何做到的呢?其实data是利用一个cache对象做为中介来实现DOM元素和对象之间的间接联系的。

<script type="text/javascript">
$("#box").attr('name',obj);
$("#box").data('name',obj);
//data可以防止内存泄漏,这是data和attr、prop方法的主要区别之一
//data是利用一个cache的中介来实现DOM元素和对象之间的间接联系
var cache = {};//这就是中介对象
<div id="box" xxx="1"></div>
<body xxx="2"></body>
/*这样其实我们的xxx属性中其实存放的是一个数字字符串
并不是一个对象,因此不会发生内存泄漏*/
//在执行完:
$("#box").data('name',obj);
$("body").data('age',obj);
//这样之后,cache变成这样了:
//1和2就是uid
//每个uid都对应一个元素的缓存数据,每个缓存对象可以由多个name/value(名值对)对组成,而value是可以是任何数据类型的
var cache = {
    1:{
    name:obj
    },
    2{
    age:obj
    }
};
}
</script>

data的实现不像attr那样直接把数据作为属性捆绑到元素节点上,通过中介对象cache来实现,在 DOM Element 上添加一个自定义属性,存放 “cache”对应的 uid。
通过这种方式能够避免DOM元素与对象之间的互相引用,从未避免内存泄漏。因为{name:obj}和{age:obj}分别存放在中介对象cache中,与DOM元素没有任何联系。 同时,在DOM元素的自定义属性中引用的只是一个字符串,而不是引用对象,因此不会发生内存泄漏。

源码简化

<script type="text/javascript">
jQuery.extend({//工具方法  既可以给原生js用,也可以给jQuery对象用
    acceptData
    hasData  //判断是否存在相应的数据,返回一个布尔值
    data  //对外的接口
    removeData
    _data  //内部使用
    _removeData //带_是私有方法
});
jQuery.fn.extend({//实例方法
    data  添加数据
    removeData   删除数据
});
Data.prototype={
    key //分配映射,防止内存泄漏
    set//设置cache中的属性
    get//获取cache中的属性
    access//set和get的合体
    remove//删除cache中的属性
    hasData//判断cache中的属性有没有
    discard //删除cache中的属性
};
//data两个参数是设置,一个参数是获取
 */
$(function(){
    //实例方法演示
    $("#box").data('name','hello');//设置属性
    $("#box").removeData('name');//删除数据
    alert($('#box').data('name'));//获取属性值

    //工具方法演示
    $.data(document.body,'age',30);//设置
    alert($.hasData(document.body,'age'));//true
    $.removeData(document.body,'age');//删除
    alert($.hasData(document.body,'age'));//false
    alert($.data(document.body,'age'));//获取
});
</script>

源码中Data()构造函数部分:
在jQuery内部创建一个cache对象{}, 来保存缓存数据。 然后往需要进行缓存的DOM节点上扩展一个值为expando的属性

function Data() {//构造函数
    // Support: Android < 4,
    // Old WebKit does not have Object.preventExtensions/freeze method,
    // return new empty object instead with no [[set]] accessor
    // 默认添加了一个0属性,默认值是{},且值不能被修改
    Object.defineProperty( this.cache = {}, 0, {
        get: function() {
            return {};
        }
    });
    //expando是一个随机字符串
    //Math.random()生成0~1之间的随机数
    //所以this.expando是一个随机性很强的数值
    this.expando = jQuery.expando + Math.random();
}

注:expando的值,用于把当前数据缓存的uid值做一个节点的属性给写入到指定的元素上形成关联桥梁,所以元素本身具有这种属性的可能性很少,所以可以忽略冲突。
freeze()方法防止对象的属性被修改

<script type="text/javascript">
var obj = {name:"hello"};
Object.freeze(obj);//这样设置后obj的name属性只能获取,不能设置
obj.name = 'hi';
alert(obj.name);
</script>
<script type="text/javascript">
var obj = {name:"hello"};
//这里是给obj添加了0这样一个属性,属性值是{}
//因为只有get方法,没有set方法,所以只能获取,不能设置
Object.defineProperty( obj, 0, {
        get: function() {
            return {};
        }
    });
alert(obj[0]);//[object Object]
obj[0] = 'hi';//这里不能改变属性的值
alert(obj[0]);//[object Object]
</script>

uid是用来关联dom对象与数据缓存对象的,先在dom元素上找到expando的值,也就uid,然后通过这个uid找到数据缓存cache对象中相对应的内容。

Data.uid = 1;

接着把每个节点的dom[expando]的值都设为一个自增的变量id,保持全局唯一性。 这个id的值就作为cache的key用来关联DOM节点和数据。也就是说cache[id]就取到了这个节点上的所有缓存,即id就好比是打开一个房间(DOM节点)的钥匙。 而每个元素的所有缓存都被放到了一个map映射里面,这样可以同时缓存多个数据。

总结

通过data存储数据

  • 通过cache对象把数据对象与dom元素间接关联起来
  • 产生一个 unlock = Data.uid++; unlock 标记号
  • 把unlock标记号,作为一个属性值赋值给相应的元素节点的expando属性
  • 在cache缓存对象中开辟一个新的空间用于存储相应的数据对象this.cache[ unlock ] = {};
  • 最后把数据对象挂载到数据缓存对象cache上

通过data获取数据
从元素节点中获取到unlock标记,通过unlock在数据缓存对象cache中取到对应的数据。
元素节点上: expando:uid
数据缓存对象cache上: uid:Object
我们只需要根据对应的uid找到对应的数据对象就可以了

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值