zoukankan      html  css  js  c++  java
  • jQuery 源码 data 数据缓存

    
    data数据缓存 3308
    
    
    先做个区分:
    下面这三种方式,都可以弹出hello。
    attr使用方法。
    	$('#content-box').attr('name', 'hello');
    	alert($('#content-box').attr('name'));		//hello
    
    document.getElementById('#content-box').setAttribute('name', 'hello');
    alert(document.getElementById('#content-box').getAttribute('name'));
    
    
    prop使用方法.
    	$('#content-box').prop('name', 'hello');
    	alert($('#content-box').prop('name'));		//hello
    
    document.getElementById('#content-box').name = 'hello';
    alert(document.getElementById('#content-box').name);
    [] 和 . 是一样的。
    document.getElementById('#content-box')['name'] = 'hello';
    alert(document.getElementById('#content-box')['name']);
    
    上面的两种方法,不适合做大量数据,适合做简单数据或者元素本身的属性。设定一个class,id等。
    但是要加载很多数据的时候,就用data挂载大量的数据。
    
    data使用方法.
    	$('#content-box').data('name', 'hello');
    	alert($('#content-box').data('name'));		//hello
    
    讨论一下JS内存泄漏的问题。
    	变量不用的时候,浏览器有自动的垃圾回收机制,去销毁掉。这个变量就消失了。内存释放。
    JS有几种方式可以引起内存泄漏的。
    	其中一种就是dom元素与对象之间互相引用。大部分浏览器就会出现内存泄漏。
    
    DOM是如何与对象间产生内存泄漏的。
    举个例子:
    		
    	html
    	<div id='test' class='content-box'></div>
    
    	JS
    	var oDiv = document.getElementById('test');
    	var obj = {};
    
    	oDiv.name = obj;
    	obj.age = oDiv;
    
    解释一下:oDiv是一个DOM的对象,而obj又是一个JS的对象。
    首先,给oDiv添加了一个name属性,赋值为obj,
    然后,又用obj去引用oDiv。就出现了内存泄漏。
    
    // 	这里选择器先选出了一个DMO对象,然后可能会对其中的属性,进行加载对象操作。
    	$('#content-box').attr('name', obj);
    //	有可能未来的某一行代码,就出现了obj去引用DOM的可能,那就出现了内存泄漏的这个坑。
    
    解决问题的方式,就是用data。
    	就不用担心内存泄漏的问题,data会防止DOM和对象之间的相互引用,防止内存泄漏。
    	$('#content-box').data('name', obj);
    	
    
    
    /*****************************************************/
    
    设计思路:
    	
    	data设计了一个中介,将DOM与数据(对象),间接的联系在一起。
    通过cache = {}, 来进行连接,做了一个映射。
    比如:
    $('#box1').attr('XXX', obj1);
    $('#box2').attr('YYY', obj2);
    $('#box3').attr('ZZZ', obj3);
    
    由于选择器一定是有先后顺序的,所以,为box1的XXX属性设定一个1.
    box2的YYY属性设定一个2,box3的ZZZ属性设定一个3。都存入cache的对象中。
    
    此时:cache = {}----->>>>
    
    				转变为----->>>>>
    
    						cache = {
    							1: {
    								XXX : obj1
    							},
    							2: {
    								YYY : obj2
    							},
    							3: {
    								ZZZ : obj3
    							}
    						}
    
    而HTML中的属性----->>>>
    		<div id="box1"></div>
    		<div id="box2"></div>
    		<div id="box3"></div>						
    					转变为----->>>>>
    							<div id="box1" XXX='1'></div>
    							<div id="box2" YYY='2'></div>
    							<div id="box3" ZZZ='3'></div>
    
    								这样, XXX可以通过1,来找到cache对象中的响应的属性值。
    									[XXX ---> 1]
    									[YYY ---> 2]
    									[ZZZ ---> 3]
    
    利用这种方式,可以防止内存泄漏。这回将数据存入Json中,也就是cache中。
    跟DOM就没关系了。因为dom中存放的是一个字符串,也就是键值对的形式。反正不是对象。
    通过cache建立了链接,给dom上面挂载数据。
    
    jQuery的小习惯。
    	$('div').html('hello');		//		可以设定一堆
    	但是,
    	alert($('div').html());		//   	只返回一个。
    
    
    /*****************************************************/
    
    结构:
    	Data.prototype = {
    		key 			// 	分配映射的。类似有搞定键值对。
    		set   			//	设定	
    		get 			//	获取
    		access 			//	get/set的集合方法。2个参数,用get,三个参数set
    		remove          //	删除cache中元素
    		hasData 		// 	判断是否存在
    		discard 		//	删除cache中苏醒
    	}
    
    /*****************************************************/
    结构:
    	jQuery.extend({
    		acceptData	    				//	可以接受元素的范围   
    		hasData 		Data->hasData	//	判断是否有这个数据,有就返回真,没有返回假。
    		data 			Data->access	//	绑定数据,获取,设定参数。
    		removeData 		Data->remove	//  删除数据
    		_data 							//	这是私有方法。
    		_removeData						//	这是私有方法。内部使用
    	});
    这里的Data是,上面的对象。
    
    使用例子:
    extend的方法,既可以给原生的js用,也可以给jQuery对象用。
    	
    	$.data(document.body, 'age', 22);
    	alert($.data(document.body, 'age'));		//22
    	alert($.hasData(document.body, 'age'));		// true
    
    	$.removeData(document.body, 'age');
    	alert($.data(document.body, 'age'));		//undefined
    	alert($.hasData(document.body, 'age'));		// false
    
    
    	$.data(document.body, {'hg', 'wj'});	//这样也可以,里面可以处理json数据格式
    	
    /*****************************************************/
    
    结构:
    	jQuery.fn.extend({
    		data
    		removeData
    	})
    
    使用例子:
    	$('#test').data('name', 'hgwj');	// 绑定数据
    	alert($('#test').data('name'));		// hgwj
    
    	$('#test').removeData('name');		// 删除数据
    	alert($('#test').data('name'));		// undefined
    
    
    /***********************************************************************************/
    
    阅读源码:
    	
    var data_user, data_priv,
    	rbrace = /(?:{[sS]*}|[[sS]*])$/,
    	rmultiDash = /([A-Z])/g;
    
    function Data() {
    	// Support: Android < 4,
    	// Old WebKit does not have Object.preventExtensions/freeze method,
    	// return new empty object instead with no [[set]] accessor
    	// 	英文介绍,老式的webkit不支持这个方法。所以选择用这个defineProperty来代替。
    	//	preventExtensions 和 freeze 作用就是防止对象被修改。
    	//  Android < 4 不支持。ES5的方法defineProperty
    
    	Object.defineProperty( this.cache = {}, 0, {
    		get: function() {
    			return {};
    		}
    	});
    
    	//	返回一个jQuery的版本 +随机数 +  随机数的组合。
    	//	 重复的概率很低,
    	this.expando = jQuery.expando + Math.random();		
    
    	console.log(this.expando);		//jQuery203083968277710742660.6855781189593687
    	console.log(this);
    	// this 对应下面一拖数据。 有一些属性和方法,已经键值对说对应的值。	
    /*	
    	Data {cache: Object, expando: "jQuery203077120183012448250.8953614917118102"}
    	cache: Object0: (...)get 0: ()arguments: nullcaller: nulllength: 0name: ""prototype: Object.defineProperty.get__proto__: ()<function scope>1: Objectage: 22__proto__: Object__proto__: Object__defineGetter__: __defineGetter__()__defineSetter__: __defineSetter__()__lookupGetter__: __lookupGetter__()__lookupSetter__: __lookupSetter__()constructor: Object()hasOwnProperty: hasOwnProperty()isPrototypeOf: isPrototypeOf()propertyIsEnumerable: propertyIsEnumerable()toLocaleString: toLocaleString()toString: toString()valueOf: valueOf()get __proto__: get __proto__()set __proto__: set __proto__()expando: "jQuery203077120183012448250.8953614917118102"__proto__: Object
    	jquery-2.0.3.js:3323 Data {cache: Object, expando: "jQuery203077120183012448250.46686112554743886"}cache: Objectexpando: "jQuery203077120183012448250.46686112554743886"__proto__: Object
    */
    }
    
    Data.uid = 1;
    
    Data.accepts = function( owner ) {
    	// Accepts only:
    	//  - Node
    	//    - Node.ELEMENT_NODE
    	//    - Node.DOCUMENT_NODE
    	//  - Object
    	//    - Any
    	//	这里是jQuery中DOM选择器选出来的是DOM节点,如果是1 是节点, 9 是文档,
    	return owner.nodeType ?
    		owner.nodeType === 1 || owner.nodeType === 9 : true;
    };
    
    Data.prototype = {
    	key: function( owner ) {
    		// 判断有没有元素,没有就返回0.
    		if ( !Data.accepts( owner ) ) {
    			return 0;
    		}
    
    		var descriptor = {},
    			// Check if the owner object already has a cache key
    			//	看是否是第一次进来,如果是第一次,就证明没有分配过 this.expando
    			// 	unlock就是undefiend,如果分配过,证明这个元素已经不是第一次分配数据了。
    			unlock = owner[ this.expando ];	
    
    		// If not, create one
    		// 没有创建过 this.expando 属性,就创建,并且添加入节点中。
    		if ( !unlock ) {
    			unlock = Data.uid++;
    
    			// 这里就让用户无法改变的 this.expando 这个属性,
    			try {
    				descriptor[ this.expando ] = { value: unlock };
    				//	这个方法就是只能获取,不能修改。
    				Object.defineProperties( owner, descriptor );	
    
    			// 但是这里安卓4以下无法使用,就做一个兼容写法。
    			} catch ( e ) {
    				descriptor[ this.expando ] = unlock;
    				// 传统的方式添加。
    				jQuery.extend( owner, descriptor );		//将数据添加到传入的参数中。
    			}
    		}
    
    		// Ensure the cache object
    		if ( !this.cache[ unlock ] ) {
    			this.cache[ unlock ] = {};
    		}
    
    		return unlock;
    	},
    
    	// 设定数据。
    	set: function( owner, data, value ) {
    		var prop,
    			unlock = this.key( owner ),
    			cache = this.cache[ unlock ];
    
    		// Handle: [ owner, key, value ] args
    		if ( typeof data === "string" ) {
    			cache[ data ] = value;
    
    
    		// Handle: [ owner, { properties } ] args
    		} else {	//Json 的形式。 
    		//	这里说明:$.data(document.body, {'hg', 'wj'});	
    		//	这样也可以,里面可以处理json数据格式
    
    			// 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 ];
    				}
    			}
    		}
    		return cache;
    	},
    
    	//获取
    	get: function( owner, key ) {
    
    		// 跟设定一样,都是通过这个key去找到id。
    		var cache = this.cache[ this.key( owner ) ];
    
    		return key === undefined ?
    			cache : cache[ key ];
    	},
    
    	// 根据参数的个数,整合一下 get 和 set
    	access: function( owner, key, value ) {
    		var stored;
    		// 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) ) {
    
    			stored = this.get( owner, key );
    
    			return stored !== undefined ?
    				stored : this.get( owner, jQuery.camelCase(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;
    	},
    
    	// 	删除。
    	remove: function( owner, key ) {
    		var i, name, camel,
    			unlock = this.key( owner ),
    			cache = this.cache[ unlock ];
    
    		// 不存在key的时候,就全部都干掉。
    		if ( key === undefined ) {	
    			this.cache[ unlock ] = {};
    
    		} 
    		// 有key的时候。
    		else {
    			// Support array or space separated string of keys
    			if ( jQuery.isArray( key ) ) {
    				// If "name" is an array of keys...
    				// When data is initially created, via ("key", "val") signature,
    				// keys will be converted to camelCase.
    				// Since there is no way to tell _how_ a key was added, remove
    				// both plain key and camelCase key. #12786
    				// This will only penalize the array argument path.
    				//		这里返回一个驼峰的写法
    				// 		也就是说这里的 (hg-wj === hgWj),这两个是等价的。
    				name = key.concat( key.map( jQuery.camelCase ) );	
    			} else {
    				camel = jQuery.camelCase( key );
    				// Try the string as a key before any manipulation
    				if ( key in cache ) {
    					name = [ key, camel ];
    				} else {
    					// If a key with the spaces exists, use it.
    					// Otherwise, create an array by matching non-whitespace
    					name = camel;
    					//	这里处理了,传入的东西,根本就没有,无法返回。
    					name = name in cache ?
    						[ name ] : ( name.match( core_rnotwhite ) || [] );
    				}
    			}
    
    			i = name.length;
    			while ( i-- ) {
    				delete cache[ name[ i ] ];
    			}
    		}
    	},
    	hasData: function( owner ) {
    		return !jQuery.isEmptyObject(
    			this.cache[ owner[ this.expando ] ] || {}
    		);
    	},
    	// 删除的是一个整体, 把这个 this.expando 给干掉了。 
    	discard: function( owner ) {
    		if ( owner[ this.expando ] ) {
    			delete this.cache[ owner[ this.expando ] ];
    		}
    	}
    };
    
    // These may be used throughout the jQuery core codebase
    data_user = new Data();
    data_priv = new Data();
    
    
    
    例子: (1)
    解释一下 defineProperty 和 freeze
    
    Object.freeze(obj);这个函数,导致了obj无法被修改,只能阅读。
    
    	var obj = {name: 'hg'};
    	obj.name = 'wj';
    	console.log(obj.name);		//wj
    	Object.freeze(obj);
    	obj.name = 'hg';
    	console.log(obj.name);		//还是wj
    
    
    Object.defineProperty(obj);
    	/*三个参数。
    		(1)	要锁定的对象,
    		(2)	建立了 一个 对应索引值。
    			0 对应了一个索引 var obj = {name: 'hg', 0 : {123} };
    			给obj对象添加了一个key 和 返回值 value 。
    			这么写,因为没有set方法,只能get方法,不能设置,只能获取。
    		(3) 操作对象数据。
    	*/
    	var obj = {name: 'hg'};
    	obj.name = 'wj';
    	Object.defineProperty(obj, 0, {
    		get : function () {
    			return {0:123};
    		},
    	});
    
    	console.log(obj[0]);	//Object {0: 123}
    	obj[0] = 123;
    	console.log(obj[0]);	//Object {0: 123} 并没有改变
    // es5 中还是有很多很不错的方法,可以玩玩。
    
    
    
    
    /***********************************************************************************/
    
    	工具方法:调用的都是data对象的方法。
    	jQuery.extend({
    		acceptData: Data.accepts,
    
    		hasData: function( elem ) {
    			return data_user.hasData( elem ) || data_priv.hasData( elem );
    		},
    
    		data: function( elem, name, data ) {
    			return data_user.access( elem, name, data );
    		},
    
    		removeData: function( elem, name ) {
    			data_user.remove( elem, name );
    		},
    
    		_data: function( elem, name, data ) {
    			return data_priv.access( elem, name, data );
    		},
    
    		_removeData: function( elem, name ) {
    			data_priv.remove( elem, name );
    		}
    	});
    
    /***********************************************************************************/
    
    	实例方法。
    	jQuery.fn.extend({
    		data: function( key, value ) {
    			var attrs, name,
    				//	获取this
    				elem = this[ 0 ],
    				i = 0,
    				data = null;
    
    			// Gets all values
    			//	判断key是否为空
    			if ( key === undefined ) {
    				// $('dsd')	--> 获取不到
    				if ( this.length ) {	//	判断元素的长度。是否有元素
    					data = data_user.get( elem );
    
    					//	这里一并判断了自定义的属性。也兼容html5中的自定义属性。
    					if ( elem.nodeType === 1 && !data_priv.get( elem, "hasDataAttrs" ) ) {
    						attrs = elem.attributes;	//	获取所有属性的一个集合。
    						for ( ; i < attrs.length; i++ ) {
    							name = attrs[ i ].name;
    
    							// 判断数据中是否包含 data-这种,给转驼峰。
    							if ( name.indexOf( "data-" ) === 0 ) {
    								name = jQuery.camelCase( name.slice(5) );
    								// 设定到cache中。
    								dataAttr( elem, name, data[ name ] );
    							}
    						}
    						data_priv.set( elem, "hasDataAttrs", true );
    					}
    				}
    
    				return data;
    			}
    
    			if ( typeof key === "object" ) {
    				return this.each(function() {
    					data_user.set( this, key );
    				});
    			}
    
    			return jQuery.access( this, function( value ) {
    				var data,
    					camelKey = jQuery.camelCase( key );
    
    				if ( elem && value === undefined ) {
    					// Attempt to get data from the cache
    					// with the key as-is
    					data = data_user.get( elem, key );
    					if ( data !== undefined ) {
    						return data;
    					}
    
    
    					data = data_user.get( elem, camelKey );
    					if ( data !== undefined ) {
    						return data;
    					}
    
    					data = dataAttr( elem, camelKey, undefined );
    					if ( data !== undefined ) {
    						return data;
    					}
    
    					// We tried really hard, but the data doesn't exist.
    					return;
    				}
    
    				// Set the data...
    				this.each(function() {
    
    					data_user.set( this, camelKey, value );
    
    					if ( key.indexOf("-") !== -1 && data !== undefined ) {
    						data_user.set( this, key, value );
    					}
    				});
    			}, null, value, arguments.length > 1, null, true );
    		},
    
    		removeData: function( key ) {
    			return this.each(function() {
    				data_user.remove( this, key );
    			});
    		}
    	});
    
    例子:
    	
    (1)
    	$('#div1').data('name', 'hg');
    	$('#div1').data('age', '21');
    	//	会打印所有的数据, {'name':'hg', 'age':'21'};
    	console.log($('#div1').data());	
    
    (2)
    	自定义数据是h5的特性。
    	<div id='hgwj' data-hgwj='hgwjtest'></div>
    	alert($('#hgwj').get(0).dataset.hgwj);		//	打印出来 hgwjtest	
    	
    	<div id='hgwj' data-hgwj-all='hgwjtest'></div>
    	alert($('#hgwj').get(0).dataset.hgwjAll);	//	打印出来 hgwjtest

    (3)
      如果存入的是这两个,那么jQuery会认为是不同的。
      $('#div1').data('nameAge', 'h1');
      $('#div1').data('name-age', 'hellow');


      this.cache = {
        1 : {
            nameAge' : 'hello',
            'name-age' : 'hello'
          }
      }

     

  • 相关阅读:
    clickhouse-(04)-常用高阶函数
    clickhouse-(03)-库和表引擎
    clickhouse-(02)-适合的场景
    clickhouse-(01)-安装
    MySQL实战45讲-笔记
    Linux软连接和硬链接
    直接访问和间接访问
    指针和地址的区别
    配置Apache 运行CGI---------笔记
    配置Apache 运行CGI
  • 原文地址:https://www.cnblogs.com/hgonlywj/p/4864636.html
Copyright © 2011-2022 走看看