一、实现原理:
对于DOM元素,通过分配一个唯一的关联id把DOM元素和该DOM元素的数据缓存对象关联起来,关联id被附加到以jQuery.expando的值命名的属性上,数据存储在全局缓存对象jQuery.cache中。在读取、设置、移除数据时,将通过关联id从全局缓存对象jQuery.cache中找到关联的数据缓存对象,然后在数据缓存对象上执行读取、设置、移除操作。
对于Javascript对象,数据则直接存储在该Javascript对象的属性jQuery.expando上。在读取、设置、移除数据时,实际上是对Javascript对象的数据缓存对象执行读取、设置、移除操作。
为了避免jQuery内部使用的数据和用户自定义的数据发生冲突,数据缓存模块把内部数据存储在数据缓存对象上,把自定义数据存储在数据缓存对象的属性data上。
二、总体结构:
// 数据缓存 Data jQuery.extend({ // 全局缓存对象 cache: {}, // 唯一 id种子 uuid:0, // 页面中每个jQuery副本的唯一标识 expando: "jQuery" + ( jQuery.fn.jquery + Math.random() ).replace( /\D/g, "" ), // 是否有关联的数据 hasData: function(){}, // 设置、读取自定数据或内部数据 data: function(elem, name, data, pvt) {}, // 移除自定义数据或内部数据 removeData: function(elem, name, pvt) {}, // 设置、读取内部数据 _data: function(elem, name, data) {}, // 是否可以设置数据 acceptData: function(elem){} }); jQuery.fn.extend({ // 设置、读取自定义数据,解析HTML5属性data- data: function(key,value){}, // 移除自定义数据 removeData: function(key){} }); // 解析HTML5属性 data- function dataAttr(elem,key,data){} // 检查数据缓存对象是否为空 function isEmptyDataObject(obj){} jQuery.extend({ // 清空数据缓存对象 cleanData: function(elems){} });
三、$.data(elem, name, data), $.data(elem, name)
$.data(elem, name, data)的使用方法:
如果传入参数name, data, 则设置任意类型的数据
<!doctype html> <html lang="en"> <head> <meta charset="utf-8"> <title>jQuery.data demo</title> <style> div { color: blue; } span { color: red; } </style> <script src="//code.jquery.com/jquery-1.10.2.js"></script> </head> <body> <div> The values stored were <span></span> and <span></span> </div> <script> var div = $( "div" )[ 0 ]; jQuery.data( div, "test", { first: 16, last: "pizza!" }); $( "span:first" ).text( jQuery.data( div, "test" ).first ); $( "span:last" ).text( jQuery.data( div, "test" ).last ); </script> </body> </html>
$.data(elem, name)的使用方法:
如果传入key, 未传入参数data, 则读取并返回指定名称的数据
<!doctype html> <html lang="en"> <head> <meta charset="utf-8"> <title>jQuery.data demo</title> <style> div { margin: 5px; background: yellow; } button { margin: 5px; font-size: 14px; } p { margin: 5px; color: blue; } span { color: red; } </style> <script src="//code.jquery.com/jquery-1.10.2.js"></script> </head> <body> <div>A div</div> <button>Get "blah" from the div</button> <button>Set "blah" to "hello"</button> <button>Set "blah" to 86</button> <button>Remove "blah" from the div</button> <p>The "blah" value of this div is <span>?</span></p> <script> $( "button" ).click( function() { var value, div = $( "div" )[ 0 ]; switch ( $( "button" ).index( this ) ) { case 0 : value = jQuery.data( div, "blah" ); break; case 1 : jQuery.data( div, "blah", "hello" ); value = "Stored!"; break; case 2 : jQuery.data( div, "blah", 86 ); value = "Stored!"; break; case 3 : jQuery.removeData( div, "blah" ); value = "Removed!"; break; } $( "span" ).text( "" + value ); }); </script> </body> </html>
$.data(elem, name, data), $.data(elem, name) 源码解析:
jQuery.extend({ // 1. 定义jQuery.data(elem, name, data, pvt) data: function( elem, name, data, pvt /* Internal Use Only */ ) { // 2. 检查是否可以设置数据 if ( !jQuery.acceptData( elem ) ) { return; // 如果参数elem不支持设置数据,则立即返回 } // 3 定义局部变量 var privateCache, thisCache, ret, internalKey = jQuery.expando, getByName = typeof name === "string", // We have to handle DOM nodes and JS objects differently because IE6-7 // can't GC object references properly across the DOM-JS boundary isNode = elem.nodeType, // elem是否是DOM元素 // Only DOM nodes need the global jQuery cache; JS object data is // attached directly to the object so GC can occur automatically cache = isNode ? jQuery.cache : elem, // 如果是DOM元素,为了避免javascript和DOM元素之间循环引用导致的浏览器(IE6/7)垃圾回收机制不起作用,要把数据存储在全局缓存对象jQuery.cache中;对于javascript对象,来及回收机制能够自动发生,不会有内存泄露的问题,因此数据可以查收存储在javascript对象上 // Only defining an ID for JS objects if its cache already exists allows // the code to shortcut on the same path as a DOM node with no cache id = isNode ? elem[ internalKey ] : elem[ internalKey ] && internalKey, isEvents = name === "events"; // Avoid doing any more work than we need to when trying to get data on an // object that has no data at all // 4. 如果是读取数据,但没有数据,则返回 if ( (!id || !cache[id] || (!isEvents && !pvt && !cache[id].data)) && getByName && data === undefined ) { return; // getByName && data === undefined 如果name是字符串,data是undefined, 说明是在读取数据 // !id || !cache[id] || (!isEvents && !pvt && !cache[id].data 如果关联id不存在,说明没有数据;如果cache[id]不存在,也说明没有数据;如果是读取自动以数据,但cache[id].data不存在,说明没有自定义数据 } // 5. 如果关联id不存在,则分配一个 if ( !id ) { // Only DOM nodes need a new unique ID for each element since their data // ends up in the global cache if ( isNode ) { elem[ internalKey ] = id = ++jQuery.uuid; // 对于DOM元素,jQuery.uuid会自动加1,并附加到DOM元素上 } else { id = internalKey; // 对于javascript对象,关联id就是jQuery.expando } } // 6. 如果数据缓存对象不存在,则初始化为空对象{} if ( !cache[ id ] ) { cache[ id ] = {}; // Avoids exposing jQuery metadata on plain JS objects when the object // is serialized using JSON.stringify if ( !isNode ) { cache[ id ].toJSON = jQuery.noop; // 对于javascript对象,设置方法toJSON为空函数,以避免在执行JSON.stringify()时暴露缓存数据。如果一个对象定义了方法toJSON(),JSON.stringify()在序列化该对象时会调用这个方法来生成该对象的JSON元素 } } // An object can be passed to jQuery.data instead of a key/value pair; this gets // shallow copied over onto the existing cache // 7. 如果参数name是对象或函数,则批量设置数据 if ( typeof name === "object" || typeof name === "function" ) { if ( pvt ) { cache[ id ] = jQuery.extend( cache[ id ], name ); // 对于内部数据,把参数name中的属性合并到cache[id]中 } else { cache[ id ].data = jQuery.extend( cache[ id ].data, name ); // 对于自定义数据,把参数name中的属性合并到cache[id].data中 } } // 8. 如果参数data不是undefined, 则设置单个数据 privateCache = thisCache = cache[ id ]; // jQuery data() is stored in a separate object inside the object's internal data // cache in order to avoid key collisions between internal data and user-defined // data. if ( !pvt ) { if ( !thisCache.data ) { thisCache.data = {}; } thisCache = thisCache.data; } if ( data !== undefined ) { thisCache[ jQuery.camelCase( name ) ] = data; } // Users should not attempt to inspect the internal events object using jQuery.data, // it is undocumented and subject to change. But does anyone listen? No. // 9. 特殊处理events if ( isEvents && !thisCache[ name ] ) { // 如果参数name是字符串"events",并且未设置过自定义数据"events",则返回事件婚车对象,在其中存储了事件监听函数。 return privateCache.events; } // Check for both converted-to-camel and non-converted data property names // If a data property was specified //10. 如果参数name是字符串,则读取单个数据 if ( getByName ) { // First Try to find as-is property data ret = thisCache[ name ]; // 先尝试读取参数name对应的数据 // Test for null|undefined property data if ( ret == null ) { // 如果未取到,则把参数name转换为驼峰式再次尝试读取对应的数据 // Try to find the camelCased property ret = thisCache[ jQuery.camelCase( name ) ]; } } else { // 11. 如果未传入参数name,data,则返回数据缓存对象 ret = thisCache; } return ret; }, // For internal use only. _data: function( elem, name, data ) { return jQuery.data( elem, name, data, true ); }, });
四、.data(key, value), .data(key)
使用方法:
$( "body" ).data( "foo", 52 ); // 传入key, value $( "body" ).data( "bar", { myType: "test", count: 40 } ); // 传入key, value $( "body" ).data( { baz: [ 1, 2, 3 ] } ); // 传入key, value $( "body" ).data( "foo" ); // 52 // 传入key $( "body" ).data(); // 未传入参数
HTML5 data attriubutes:
<div data-role="page" data-last-value="43" data-hidden="true" data-options='{"name":"John"}'></div> $( "div" ).data( "role" ) === "page"; $( "div" ).data( "lastValue" ) === 43; $( "div" ).data( "hidden" ) === true; $( "div" ).data( "options" ).name === "John";
.data(key, value), .data(key) 源码解析
jQuery.fn.extend({ // 1. 定义.data(key, value) data: function( key, value ) { var parts, attr, name, data = null; // 2. 未传入参数的情况 if ( typeof key === "undefined" ) { if ( this.length ) { // 如果参数key是undefined, 即参数格式是.data(), 则调用方法jQuery.data(elem, name, data, pvt)获取第一个匹配元素关联的自定义数据缓存对象,并返回。 data = jQuery.data( this[0] ); if ( this[0].nodeType === 1 && !jQuery._data( this[0], "parsedAttrs" ) ) { attr = this[0].attributes; for ( var i = 0, l = attr.length; i < l; i++ ) { name = attr[i].name; if ( name.indexOf( "data-" ) === 0 ) { name = jQuery.camelCase( name.substring(5) ); dataAttr( this[0], name, data[ name ] ); } } jQuery._data( this[0], "parsedAttrs", true ); } } return data; // 3. 参数key 是对象的情况,即参数格式是.data(key),则遍历匹配元素集合,为每个匹配元素调用方法jQuery.data(elem, name, data,pvt)批量设置数据 } else if ( typeof key === "object" ) { return this.each(function() { jQuery.data( this, key ); }); } // 4. 只传入参数key的情况 如果只传入参数key, 即参数格式是.data(key),则返回第一个匹配元素的指定名称数据 parts = key.split("."); parts[1] = parts[1] ? "." + parts[1] : ""; if ( value === undefined ) { data = this.triggerHandler("getData" + parts[1] + "!", [parts[0]]); // Try to fetch any internally stored data first if ( data === undefined && this.length ) { data = jQuery.data( this[0], key ); data = dataAttr( this[0], key, data ); } return data === undefined && parts[1] ? this.data( parts[0] ) : data; // 5. 传入参数key和value的情况 即参数格式是.data(key, value),则为每个匹配元素设置任意类型的数据,并触发自定义事件setData, changeData } else { return this.each(function() { var self = jQuery( this ), args = [ parts[0], value ]; self.triggerHandler( "setData" + parts[1] + "!", args ); jQuery.data( this, key, value ); self.triggerHandler( "changeData" + parts[1] + "!", args ); }); } }, removeData: function( key ) { return this.each(function() { jQuery.removeData( this, key ); }); } }); // 6. 函数dataAttr(elem, key, data)解析HTML5属性data- function dataAttr( elem, key, data ) { // If nothing was found internally, try to fetch any // data from the HTML5 data-* attribute // 只有参数data为undefined时,才会解析HTML5属性data- if ( data === undefined && elem.nodeType === 1 ) { var name = "data-" + key.replace( rmultiDash, "-$1" ).toLowerCase(); data = elem.getAttribute( name ); if ( typeof data === "string" ) { try { data = data === "true" ? true : data === "false" ? false : data === "null" ? null : jQuery.isNumeric( data ) ? parseFloat( data ) : rbrace.test( data ) ? jQuery.parseJSON( data ) : data; } catch( e ) {} // Make sure we set the data so it isn't changed later jQuery.data( elem, key, data ); } else { data = undefined; } } return data; }
五、$.removeData(elem, name),.removeData(key)
使用方法:
<!doctype html> <html lang="en"> <head> <meta charset="utf-8"> <title>jQuery.removeData demo</title> <style> div { margin: 2px; color: blue; } span { color: red; } </style> <script src="//code.jquery.com/jquery-1.10.2.js"></script> </head> <body> <div>value1 before creation: <span></span></div> <div>value1 after creation: <span></span></div> <div>value1 after removal: <span></span></div> <div>value2 after removal: <span></span></div> <script> var div = $( "div" )[ 0 ]; $( "span:eq(0)" ).text( "" + $( "div" ).data( "test1" ) ); //undefined jQuery.data( div, "test1", "VALUE-1" ); jQuery.data( div, "test2", "VALUE-2" ); $( "span:eq(1)" ).text( "" + jQuery.data( div, "test1" ) ); // VALUE-1 jQuery.removeData( div, "test1" ); $( "span:eq(2)" ).text( "" + jQuery.data( div, "test1" ) ); // undefined $( "span:eq(3)" ).text( "" + jQuery.data( div, "test2" ) ); // value2 </script> </body> </html>
<!doctype html> <html lang="en"> <head> <meta charset="utf-8"> <title>removeData demo</title> <style> div { margin: 2px; color: blue; } span { color: red; } </style> <script src="//code.jquery.com/jquery-1.10.2.js"></script> </head> <body> <div>value1 before creation: <span></span></div> <div>value1 after creation: <span></span></div> <div>value1 after removal: <span></span></div> <div>value2 after removal: <span></span></div> <script> $( "span:eq(0)" ).text( "" + $( "div" ).data( "test1" ) ); // undefined $( "div" ).data( "test1", "VALUE-1" ); $( "div" ).data( "test2", "VALUE-2" ); $( "span:eq(1)" ).text( "" + $( "div").data( "test1" ) ); // VALUE-1 $( "div" ).removeData( "test1" ); $( "span:eq(2)" ).text( "" + $( "div" ).data( "test1" ) ); // undefined $( "span:eq(3)" ).text( "" + $( "div" ).data( "test2" ) ); // VALUE-2 </script> </body> </html>
$.removeData(elem, name),.removeData(key) 源码解析:
$.extend({ // jQuery.removeData(elem,name,pvt)用于移除通过jQuery.data()设置的数据 removeData: function( elem, name, pvt /* Internal Use Only */ ) { if ( !jQuery.acceptData( elem ) ) { return; } var thisCache, i, l, // Reference to internal data cache key internalKey = jQuery.expando, isNode = elem.nodeType, // See jQuery.data for more information cache = isNode ? jQuery.cache : elem, // See jQuery.data for more information id = isNode ? elem[ internalKey ] : internalKey; // If there is already no cache entry for this object, there is no // purpose in continuing if ( !cache[ id ] ) { return; } // 如果传入参数name, 则移除一个或多个数据 if ( name ) { thisCache = pvt ? cache[ id ] : cache[ id ].data; if ( thisCache ) { // 只有数据缓存对象thisCache存在时,才有必要移除数据 // Support array or space separated string names for data keys if ( !jQuery.isArray( name ) ) { // try the string as a key before any manipulation if ( name in thisCache ) { name = [ name ]; } else { // split the camel cased version by spaces unless a key with the spaces exists name = jQuery.camelCase( name ); if ( name in thisCache ) { name = [ name ]; } else { name = name.split( " " ); } } } // 遍历参数name中的数据名,用运算符delete逐个从数据缓存对象thisCache中移除 for ( i = 0, l = name.length; i < l; i++ ) { delete thisCache[ name[i] ]; } // If there is no data left in the cache, we want to continue // and let the cache object itself get destroyed if ( !( pvt ? isEmptyDataObject : jQuery.isEmptyObject )( thisCache ) ) { return; } } } // See jQuery.data for more information // 删除自定义数据缓存对象cache[id].data if ( !pvt ) { delete cache[ id ].data; // Don't destroy the parent cache unless the internal data object // had been the only thing left in it if ( !isEmptyDataObject(cache[ id ]) ) { return; } } // Browsers that fail expando deletion also refuse to delete expandos on // the window, but it will allow it on all other JS objects; other browsers // don't care // Ensure that `cache` is not a window object #10080 // 删除数据缓存对象cache[id] if ( jQuery.support.deleteExpando || !cache.setInterval ) { delete cache[ id ]; } else { cache[ id ] = null; } // We destroyed the cache and need to eliminate the expando on the node to avoid // false lookups in the cache for entries that no longer exist // 删除DOM元素上扩展的jQuery.expando属性 if ( isNode ) { // IE does not allow us to delete expando properties from nodes, // nor does it have a removeAttribute function on Document nodes; // we must handle all of these cases if ( jQuery.support.deleteExpando ) { delete elem[ internalKey ]; } else if ( elem.removeAttribute ) { elem.removeAttribute( internalKey ); } else { elem[ internalKey ] = null; } } } }); jQuery.fn.extend({ removeData: function( key ) { return this.each(function() { jQuery.removeData( this, key ); }); } }); // checks a cache object for emptiness function isEmptyDataObject( obj ) { for ( var name in obj ) { // if the public data object is empty, the private is still empty if ( name === "data" && jQuery.isEmptyObject( obj[name] ) ) { continue; } if ( name !== "toJSON" ) { return false; } } return true; }
六、$.hasData(elem)
使用方法:
<!doctype html> <html lang="en"> <head> <meta charset="utf-8"> <title>jQuery.hasData demo</title> <script src="//code.jquery.com/jquery-1.10.2.js"></script> </head> <body> <p>Results: </p> <script> var $p = jQuery( "p" ), p = $p[ 0 ]; $p.append( jQuery.hasData( p ) + " " ); // false $.data( p, "testing", 123 ); $p.append( jQuery.hasData( p ) + " " ); // true $.removeData( p, "testing" ); $p.append( jQuery.hasData( p ) + " " ); // false $p.on( "click", function() {} ); $p.append( jQuery.hasData( p ) + " " ); // true $p.off( "click" ); $p.append( jQuery.hasData( p ) + " " ); // false </script> </body> </html> $.hasData(elem) 源码解析: $.extend({ hasData: function( elem ) { elem = elem.nodeType ? jQuery.cache[ elem[jQuery.expando] ] : elem[ jQuery.expando ]; return !!elem && !isEmptyDataObject( elem ); // 如果关联的数据缓存对象存在,并且含有数据,则返回true, 否则返回false。 这里用两个逻辑非运算符! 把变量elem转换为布尔值 } });

JavaScript는 웹 페이지의 상호 작용과 역학을 향상시키기 때문에 현대 웹 사이트의 핵심입니다. 1) 페이지를 새로 고치지 않고 콘텐츠를 변경할 수 있습니다. 2) Domapi를 통해 웹 페이지 조작, 3) 애니메이션 및 드래그 앤 드롭과 같은 복잡한 대화식 효과를 지원합니다. 4) 성능 및 모범 사례를 최적화하여 사용자 경험을 향상시킵니다.

C 및 JavaScript는 WebAssembly를 통한 상호 운용성을 달성합니다. 1) C 코드는 WebAssembly 모듈로 컴파일되어 컴퓨팅 전력을 향상시키기 위해 JavaScript 환경에 도입됩니다. 2) 게임 개발에서 C는 물리 엔진 및 그래픽 렌더링을 처리하며 JavaScript는 게임 로직 및 사용자 인터페이스를 담당합니다.

JavaScript는 웹 사이트, 모바일 응용 프로그램, 데스크탑 응용 프로그램 및 서버 측 프로그래밍에서 널리 사용됩니다. 1) 웹 사이트 개발에서 JavaScript는 HTML 및 CSS와 함께 DOM을 운영하여 동적 효과를 달성하고 jQuery 및 React와 같은 프레임 워크를 지원합니다. 2) 반응 및 이온 성을 통해 JavaScript는 크로스 플랫폼 모바일 애플리케이션을 개발하는 데 사용됩니다. 3) 전자 프레임 워크를 사용하면 JavaScript가 데스크탑 애플리케이션을 구축 할 수 있습니다. 4) node.js는 JavaScript가 서버 측에서 실행되도록하고 동시 요청이 높은 높은 요청을 지원합니다.

Python은 데이터 과학 및 자동화에 더 적합한 반면 JavaScript는 프론트 엔드 및 풀 스택 개발에 더 적합합니다. 1. Python은 데이터 처리 및 모델링을 위해 Numpy 및 Pandas와 같은 라이브러리를 사용하여 데이터 과학 및 기계 학습에서 잘 수행됩니다. 2. 파이썬은 간결하고 자동화 및 스크립팅이 효율적입니다. 3. JavaScript는 프론트 엔드 개발에 없어서는 안될 것이며 동적 웹 페이지 및 단일 페이지 응용 프로그램을 구축하는 데 사용됩니다. 4. JavaScript는 Node.js를 통해 백엔드 개발에 역할을하며 전체 스택 개발을 지원합니다.

C와 C는 주로 통역사와 JIT 컴파일러를 구현하는 데 사용되는 JavaScript 엔진에서 중요한 역할을합니다. 1) C는 JavaScript 소스 코드를 구문 분석하고 추상 구문 트리를 생성하는 데 사용됩니다. 2) C는 바이트 코드 생성 및 실행을 담당합니다. 3) C는 JIT 컴파일러를 구현하고 런타임에 핫스팟 코드를 최적화하고 컴파일하며 JavaScript의 실행 효율을 크게 향상시킵니다.

실제 세계에서 JavaScript의 응용 프로그램에는 프론트 엔드 및 백엔드 개발이 포함됩니다. 1) DOM 운영 및 이벤트 처리와 관련된 TODO 목록 응용 프로그램을 구축하여 프론트 엔드 애플리케이션을 표시합니다. 2) Node.js를 통해 RESTFULAPI를 구축하고 Express를 통해 백엔드 응용 프로그램을 시연하십시오.

웹 개발에서 JavaScript의 주요 용도에는 클라이언트 상호 작용, 양식 검증 및 비동기 통신이 포함됩니다. 1) DOM 운영을 통한 동적 컨텐츠 업데이트 및 사용자 상호 작용; 2) 사용자가 사용자 경험을 향상시키기 위해 데이터를 제출하기 전에 클라이언트 확인이 수행됩니다. 3) 서버와의 진실한 통신은 Ajax 기술을 통해 달성됩니다.

보다 효율적인 코드를 작성하고 성능 병목 현상 및 최적화 전략을 이해하는 데 도움이되기 때문에 JavaScript 엔진이 내부적으로 작동하는 방식을 이해하는 것은 개발자에게 중요합니다. 1) 엔진의 워크 플로에는 구문 분석, 컴파일 및 실행; 2) 실행 프로세스 중에 엔진은 인라인 캐시 및 숨겨진 클래스와 같은 동적 최적화를 수행합니다. 3) 모범 사례에는 글로벌 변수를 피하고 루프 최적화, Const 및 Lets 사용 및 과도한 폐쇄 사용을 피하는 것이 포함됩니다.


핫 AI 도구

Undresser.AI Undress
사실적인 누드 사진을 만들기 위한 AI 기반 앱

AI Clothes Remover
사진에서 옷을 제거하는 온라인 AI 도구입니다.

Undress AI Tool
무료로 이미지를 벗다

Clothoff.io
AI 옷 제거제

Video Face Swap
완전히 무료인 AI 얼굴 교환 도구를 사용하여 모든 비디오의 얼굴을 쉽게 바꾸세요!

인기 기사

뜨거운 도구

mPDF
mPDF는 UTF-8로 인코딩된 HTML에서 PDF 파일을 생성할 수 있는 PHP 라이브러리입니다. 원저자인 Ian Back은 자신의 웹 사이트에서 "즉시" PDF 파일을 출력하고 다양한 언어를 처리하기 위해 mPDF를 작성했습니다. HTML2FPDF와 같은 원본 스크립트보다 유니코드 글꼴을 사용할 때 속도가 느리고 더 큰 파일을 생성하지만 CSS 스타일 등을 지원하고 많은 개선 사항이 있습니다. RTL(아랍어, 히브리어), CJK(중국어, 일본어, 한국어)를 포함한 거의 모든 언어를 지원합니다. 중첩된 블록 수준 요소(예: P, DIV)를 지원합니다.

VSCode Windows 64비트 다운로드
Microsoft에서 출시한 강력한 무료 IDE 편집기

메모장++7.3.1
사용하기 쉬운 무료 코드 편집기

PhpStorm 맥 버전
최신(2018.2.1) 전문 PHP 통합 개발 도구

ZendStudio 13.5.1 맥
강력한 PHP 통합 개발 환경
