問題1:jQuery內部的資料保存的格式是什麼,透過data保存的資料和註冊的事件格式有什麼區別?
首先看下面的程式碼:
$._data($("#data")[0],"name","qinliang"); $._data($("#data")[0],"sex","male"); $("#data").click(function() { console.log("click1"); }); $("#data").click(function() { console.log("click2"); }); $("#data").mouseover(function() { console.log("click"); }); //data保存的数据通过result.name,result.sex访问 //在DOM上面绑定的事件函数如果要访问,可以通过result.events.click,result.events.mouseover不过返回的都是数组 //特别注意:这里是通过_data方法保存的数据! var result=$._data($("#data")[0]); console.log(result);
note :透過該圖片我們知道,透過_data儲存的資料是儲存在DOM物件上面的,透過$._data(document.getElementById("data"))取得到的物件透過鍵名就可以存取;但對於事件綁定的函數,只能透過$._data(document.getElementById("data")).events["click"]才能存取。
問題2:使用者透過$.data儲存的資料格式是如何的?
我們修改上面的程式碼為:
$.data($("#data")[0],"name","qinliang"); $.data($("#data")[0],"sex","male"); $("#data").click(function() { console.log("click1"); }); $("#data").click(function() { console.log("click2"); }); $("#data").mouseover(function() { console.log("click"); }); //data保存的数据通过result.name,result.sex访问 //在DOM上面绑定的事件函数如果要访问,可以通过result.events.click,result.events.mouseover不过返回的都是数组 //特别注意:这里是通过_data方法保存的数据! var result=$.data($("#data")[0]); console.log(result);
note:透過該圖,你會發現用戶保存的數據,也就是透過$.data方法保存的數據只會保存自訂數據,而像事件註冊等屬於內部的數據,這部分數據不在用戶數據範圍之內!
問題3:那些數據是jQuery內部數據,那些數據是用戶自訂數據?
解答:像事件event註冊或_data是透過jQuery內部調用的,保存的數據格式就是第一張圖,而使用者透過data保存的資料是第二張圖的格式,這種格式只包含使用者自訂的資料!
#問題4:我們在jQuery實例上綁定的數據在底層是如何保存的?和透過jQuery保存資料格式是否一樣?
我們把上面的例子修改為實例方法:
$("#data").data("name","qinliang"); $("#data").data("sex","male"); $("#data").click(function() { console.log("click1"); }); $("#data").click(function() { console.log("click2"); }); $("#data").mouseover(function() { }) //先获取jQuery.expando才能去获取钥匙,所以jQuery.expando是最重要的,他是打开大门的第一步! var internalKey=jQuery.expando; //通过jQuery.expando去获取钥匙 var id= $("#data")[0][internalKey]; //获取到钥匙以后,就可以去jQuery.cache这个仓库去获取到数据了! var result=jQuery.cache[id]; //获取到的数据 console.log(result);
note:透過該圖我們知道,實例物件底層保存的資料格式具有三個域,分別為data域保存使用者的資料;events保存的是物件的事件;handler域就是通用的回呼函數!
問題5:jQuery.expando是什麼鬼?
expando: "jQuery" + ( version + Math.random() ).replace( /\D/g, "" )//每次保存数据的时候会生产一个expando?,具有随机性?
每次儲存資料的時候都會產生隨機數?其實不是的,他只會在第一次呼叫的時候產生一次,這一點一定要注意,所以不管你呼叫多少次data方法,其值都是一樣的!
下面的程式碼,產生的隨機數都是一樣的.
var result1=jQuery.expando; var result2=jQuery.expando; console.log(result1); console.log(result2);//result1和result2是相等的,因为只在第一次调用的时候产生一次随机数,以后就是直接访问前面的随机数了,因为是全局变量非函数!
note:所有的DOM都是以相同的expando來保存資料的,也就是每一個DOM都有相同的屬性名jQuery.expando!
問題5:既然expando是唯一的,那麼怎麼使得每一個DOM都有不同的鑰匙?
if ( isNode ) { id = elem[ internalKey ] = deletedIds.pop() || jQuery.guid++; } else { id = internalKey; }
note:這一段程式碼告訴我們,雖然屬性名稱是相同的,但是屬性值是不一樣的,而且是透過jQuery.guid來保證唯一性的。
問題6:jQuery.guid是什麼鬼?他為啥能夠保證每一個DOM元素具有不重複的鑰匙?
proxy函數產生新的函數的時候會處理guid,讓他的值遞增:
proxy: function( fn, context ) { var args, proxy, tmp; if ( typeof context === "string" ) { tmp = fn[ context ]; context = fn; fn = tmp; } // Quick check to determine if target is callable, in the spec // this throws a TypeError, but we will just return undefined. if ( !jQuery.isFunction( fn ) ) { return undefined; } // Simulated bind args = slice.call( arguments, 2 ); proxy = function() { return fn.apply( context || this, args.concat( slice.call( arguments ) ) ); }; // Set the guid of unique handler to the same of original handler, so it can be removed proxy.guid = fn.guid = fn.guid || jQuery.guid++;//对新的函数设置guid++保证每一个通过jQuery处理的函数都有一个guid值,而且唯一! return proxy; }
jQuery.event.add方法中,對每一個綁定的函數都會設定一個新的guid,前提是他沒有guid值:
if ( !handler.guid ) {//没有guid值添加 handler.guid = jQuery.guid++; }
在實例在on函數中,如果一個函數只會被呼叫一次,那麼設定guid值(其實就是proxy函數)
if ( one === 1 ) { origFn = fn; fn = function( event ) { // Can use an empty set, since event contains the info jQuery().off( event ); return origFn.apply( this, arguments ); }; // Use same guid so caller can remove using origFn fn.guid = origFn.guid || ( origFn.guid = jQuery.guid++ );//为函数都唯一生产guid,其它函数通过调用上面的jQuery.event.add保证唯一! }
實例儲存資料的時候,為每個DOM物件的相同的屬性,即jQuery.expando屬性設定一個唯一的鑰匙值:
if ( isNode ) { id = elem[ internalKey ] = deletedIds.pop() || jQuery.guid++; } else { id = internalKey; }
note:jQuery.guid的作用是用於事件綁定中和資料保存中,為每一個保存的資料的DOM和每一個綁定事件的回調函數分配一個唯一的guid值!
問題7:鑰匙獲取到了,那麼倉庫在那裡?
jQuery.cache={}, cache = isNode ? jQuery.cache : elem,
從這裡我們知道,如果是DOM對象,那麼倉庫就是jQuery.cache,如果是js對像那麼倉庫就是自身!
問題8:鑰匙獲取到了,倉庫也有了,可以獲取到數據了,那麼我的數據是保存在那裡的?
上面我們說過:實例物件底層保存的資料格式具有三個域,分別為data域保存使用者的資料;events保存的是物件的事件;handler域就是通用的回呼函數!那麼我的資料是如何保存到data域下面的:
if ( !cache[ id ] ) {//如果DOM的仓库还没存在,也就是没调用过data方法,那么我们初始化仓库! // Avoid exposing jQuery metadata on plain JS objects when the object // is serialized using JSON.stringify cache[ id ] = isNode ? {} : { toJSON: jQuery.noop }; } // An object can be passed to jQuery.data instead of a key/value pair; this gets // shallow copied over onto the existing cache if ( typeof name === "object" || typeof name === "function" ) {//如果是object或者function那么我们让仓库继承他所有的属性 if ( pvt ) { cache[ id ] = jQuery.extend( cache[ id ], name );//jQuery内部的数据直接保存在仓库里面 } else { cache[ id ].data = jQuery.extend( cache[ id ].data, name );//如果是自定义数据保存在仓库的data域下面! } }
note:用戶資料保存在倉庫的data域下面,如果是jQuery資料保存在倉庫裡就可以了,不用重新開啟一個空間!
問題9:如何判斷是獲取數據還是保存數據?
//获取到数据仓库,cache是仓库,id是钥匙! 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. //如果是用户数据,而且没有data域,那么开启data域 if ( !pvt ) { if ( !thisCache.data ) { thisCache.data = {}; } thisCache = thisCache.data; } //到达这里,如果是用户数据已经获取到仓库的data域,内部数据获取到仓库! if ( data !== undefined ) { //保存数据成功! thisCache[ jQuery.camelCase( name ) ] = data; } // Check for both converted-to-camel and non-converted data property names // If a data property was specified //如果name是string那么获取仓库中数据返回,否则直接返回仓库本身! if ( typeof name === "string" ) { // First Try to find as-is property data ret = thisCache[ name ]; // Test for null|undefined property data if ( ret == null ) { // Try to find the camelCased property ret = thisCache[ jQuery.camelCase( name ) ]; } } else { ret = thisCache; } return ret;
note:如果data存在,表示是保存數據,這時候把數據保存到特定的域下面。使用者資料在倉庫的data網域下面,內部資料直接在倉庫裡面!;如果是取得數據,如果name是string那麼取得倉庫中特定的內容,否則直接傳回倉庫本身!
問題10:是否可以在Object物件上面儲存資料?
var obj=new Object();//在object对象上面保存数据! jQuery.data(obj,"name","qinliang"); jQuery.data(obj,"sex","female"); var result=jQuery.data(obj,"sex"); console.log(result);//打印sex
note:這種儲存資料的方式不是DOM觸發的,而是JS物件完成的。然而data方法屬於jQuery對象,所以無法直接呼叫data方法,但是可以呼叫jQuery.data方法!這種方式也可以在Object對像上面保存資料!我們來看看這種方式保存資料格式是怎麼樣的:
var obj=new Object(); jQuery.data(obj,"name","qinliang"); var key=jQuery.expando; //对于Object对象,钥匙就是jQuery.expando var warehouse=obj; //获取仓库,Object对象的仓库是自身 var data= warehouse[key]; //获取数据 console.log(data);
note:通过该图你会发现,通过object对象保存的数据也是在data域下面,同时为了保证数据的安全性,该元素的toJson方法是空的,也表示无法对数据进行json格式化!
问题11:如果是jQuery内部的数据那么不是保存在data域下面?我想确认一下?
//内部数据是只能通过jQuery._data自己调用 jQuery._data($("#data")[0],"name","qinliang"); jQuery._data($("#data")[0],"sex","male"); $("#data").click(function() { console.log("1"); }); //获取expando以求获取钥匙 var expando=jQuery.expando; //获取钥匙 var key=$("#data")[0][expando]; //获取仓库 var walhouse=jQuery.cache[key]; console.log(walhouse);
note:通过该图我们再次确认了,jQeury内部的数据都是单独保存的,没有放入data域下面进行重新组织,这一点一定要和用户自定义的数据区分开来!
问题12:内部数据和用户数据调用方式如何?
jQuery内部数据通过jQuery._data方法保存
_data: function( elem, name, data ) { return internalData( elem, name, data, true );//第四个参数为true表示保存在非data域下面! }
用户数据保存在data域下面:
data: function( elem, name, data ) { return internalData( elem, name, data ); }
问题13:我们调用实例方法如果需要设置多个属性和值,那么必须多次调用data方法?
解答:不是的,我们看看实例data方法是如何处理的
<span style="font-size:10px;">if ( typeof key === "object" ) { return this.each(function() { jQuery.data( this, key );//这说明我们如果要保存多个值,那么可以传入对象字面量 }); }</span>
那么我们在看看jQuery.data是如何支持实例方法传入对象字面量的
if ( typeof name === "object" || typeof name === "function" ) { if ( pvt ) { cache[ id ] = jQuery.extend( cache[ id ], name ); } else { cache[ id ].data = jQuery.extend( cache[ id ].data, name ); } }
note:jQuery.data支持对象字面量是通过jQuery.extend来实现的,他会把我们通过data实例方法传入的对象字面量全部继承到仓库中!
问题14:如果我们调用data方法不传入参数,或者传入参数是undefined,那是不是说返回的必定是undefined?
解答:不对,我们看看data实例方法是如何处理的
f ( key === undefined ) { if ( this.length ) { //获取第1个元素的数据 data = jQuery.data( elem ); //如果第1个调用元素的parsedAttrs属性为空! if ( elem.nodeType === 1 && !jQuery._data( elem, "parsedAttrs" ) ) { i = attrs.length; while ( i-- ) { // Support: IE11+ // The attrs elements can be null (#14894) if ( attrs[ i ] ) { name = attrs[ i ].name; if ( name.indexOf( "data-" ) === 0 ) { name = jQuery.camelCase( name.slice(5) ); //通过dataAttr方法获取到HTML5数据 //传入如dataAttr(elem,"sex",undefined) dataAttr( elem, name, data[ name ] ); } } } jQuery._data( elem, "parsedAttrs", true ); } } return data; }
note:如果没有传入参数,那么首先获取第一个DOM元素的所有的属性,同时查找该DOM元素上同名的HTML5属性,把这个数据保存到jQuery内部数据中,键名是parsedAttrs!这一点是jQuery智能操作完成的!看下面的例子:
HTML部分:
<p style="width:100px;height:100px;background-color:red;" id="data" data-sex="male" data-name="qinliang"></p>
JS部分:
//这时候传入的参数是undefined,所以最后就是把HTML5的数据保存为用户的数据,键名为HTML5键名的 //缩写版,如"sex"而不是"data-sex"。键值就是HTML5数据的值! var result=$("#data").data(); var result1=$("#data").data("name"); console.log(result1);//打印qinliang var result2=$("#data").data("sex"); console.log(result2);//打印male
note:在这里我只想说,如果用户调用data方法时候没有传入任何参数,那么jQuery会把HTML5数据作为用户数据保存,所以下次再次获取数据的时候就可以获取到了!
jQuery是如何把HTML5数据保存为用户数据的呢?
function dataAttr( elem, key, data ) { // If nothing was found internally, try to fetch any // data from the HTML5 data-* attribute //如果传入的data是undefined,同时elemenet是DOM对象,那么我们就获取HTML5上的同名属性! if ( data === undefined && elem.nodeType === 1 ) { //如果是大写就满足这个正则表达式,那么会添加两个横线并且变成小写! var name = "data-" + key.replace( rmultiDash, "-$1" ).toLowerCase(); data = elem.getAttribute( name ); if ( typeof data === "string" ) { try { //如果是true,false,null字符串那么返回非字符串形式的数据,如果不是这几种类型 data = data === "true" ? true : data === "false" ? false : data === "null" ? null : // Only convert to a number if it doesn't change the string //如果是数字那么才会转化为数子,如果是JSON类型那么转化为JSON,否则就是原样返回! +data + "" === data ? +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; }
note:我相信你不难看出,最后是通过jQuery.data把数据保存为用户数据的!注意,我们现在回到上面的那个不传入参数的情况,那么他其实是只保存了调用对象的第1个元素的数据作为用户数据,如果有多个DOM元素组成了调用对象,剩余的DOM对象是没有数据的,看下面的例子:
HTML部分
<p style="width:100px;height:100px;background-color:red;" class="data" data-sex="male" data-name="qinliang"></p> <p style="width:100px;height:100px;background-color:red;" class="data" id="me"></p>
JS部分:
//这时候传入的参数是undefined,所以最后就是把HTML5的数据保存为用户的数据,键名为HTML5键名的 //缩写版,如"sex"而不是"data-sex"。键值就是HTML5数据的值! var result=$(".data").data(); var result1=$(".data").data("name"); console.log(result1);//打印qinliang var result2=$(".data").data("sex"); console.log(result2);//打印male var result=$("#me").data(); var result1=$("#me").data("name"); console.log(result1);//打印undfined var result2=$("#me").data("sex"); console.log(result2);//打印undefined
note:虽然第一步$(".data")匹配了多个DOM元素,但是只是把第一个DOM元素的HTML5属性作为第一个DOM元素的内部数据,所以第二个DOM元素,也就是id为me的元素仍然没有name和sex属性值!
问题15:实例data源码中有parsedAttrs,是干嘛用的?
如果我们调用data方法但是没有传入参数,这时候就会存在parsedAttrs了
jQuery._data( elem, "parsedAttrs", true );//作为jQuery内部数据
其值作为内部数据保存
//这时候传入的参数是undefined,所以最后就是把HTML5的数据保存为用户的数据,键名为HTML5键名的 //缩写版,如"sex"而不是"data-sex"。键值就是HTML5数据的值! var result=$(".data").data(); //获取expando以备获取钥匙 var expando=jQuery.expando; //获取钥匙 var key=$(".data")[0][expando]; //获取仓库 var walhouse=jQuery.cache; //打开仓库获取数据 var result=walhouse[key]; console.log(result);
note:通过该图你要知道parseAttrs保存的是true,表示已经解析过HTML5属性了;但是注意:内部数据和外部数据虽然获取的方式是一样的,都是获取expando,获取钥匙,获取仓库,获取数据。但是你应该马上反应过来,上面的parseAttrs没有保存到data域下面,这就是他通过内部数据保存的结果。同时,你也应该看到内部数据和用户自定义数据在这个图上存在的共存的情况!
问题16:data如何体现了获取数据的逻辑?
return arguments.length > 1 ? // Sets one value this.each(function() { jQuery.data( this, key, value ); }) : // Gets one value // Try to fetch any internally stored data first //如果传入的参数不大于1,也就是传入单个值,因为不传值上面已经return了 //那么用dataAttr获取值,传入参数为第一个DOM对象,第二个参数是data方法传入的参数 //第三个参数是第一个DOM对象的所有通过$.data保存的数据 elem ? dataAttr( elem, key, jQuery.data( elem, key ) ) : undefined; }
note:jQuery.data(elem,key)就是获取元素上指定键名的键值数据,如果键值存在不是undefined那么dataAttr不做任何处理,直接返回;如果键值不存在那么dataAttr就回去获取HTML5属性值,保存为用户数据的同时返回数据!所以下面这种情况是有值返回的。
HTML部分:
<p style="width:100px;height:100px;background-color:red;" id="data" data-name="qinliang"></p>
JS部分:
var result=$("#data").data("name"); console.log(result);
note:用户没有保存数据的情况下我们却获取到了数据,这就是dataAttr的功劳,他把数据保存为用户数据的同时把数据返回。
问题17:除了Object,DOM,还有没有其它对象可以用来保存数据?
看看筛选函数
if ( !jQuery.acceptData( elem ) ) { return; }
详细代码如下
noData: { "applet ": true, "embed ": true, // ...but Flash objects (which have this classid) *can* handle expandos "object ": "clsid:D27CDB6E-AE6D-11cf-96B8-444553540000" } jQuery.acceptData = function( elem ) { var noData = jQuery.noData[ (elem.nodeName + " ").toLowerCase() ], nodeType = +elem.nodeType || 1; // Do not set data on non-element DOM nodes because it will not be cleared (#8335). //如果元素不是Element同时也不是document那么直接返回false表示不能用来保存数据! return nodeType !== 1 && nodeType !== 9 ? false : //如果是元素或者为document对象可以保存数据;applet和embed不能保存数据;Flash可以保存数据! // Nodes accept data unless otherwise specified; rejection can be conditional !noData || noData !== true && elem.getAttribute("classid") === noData; };
note:Element对象,document对象,Flash对象可以保存数据,applet和embed不能保存数据!很显然JS对象是可以保存数据的,因为里面没有针对JS对象排除的代码!
问题18:如何判断一个元素是否有自己的数据?
hasData: function( elem ) { //如果是DOM对象,那么就是判断仓库是否为空的,不需要特别针对data域,elem[jQuery.expando] //是钥匙,jQuery.cache是仓库。如果是JS对象等其它对象,那么钥匙就是jQuery.expando,仓库就是自身! elem = elem.nodeType ? jQuery.cache[ elem[jQuery.expando] ] : elem[ jQuery.expando ]; //仓库非空,通过isEmptyObject来判断 return !!elem && !isEmptyDataObject( elem ); }
详细见isEmptyDataObject
function isEmptyDataObject( obj ) { var name; for ( name in obj ) { // if the public data object is empty, the private is still empty //如果data域是空的,那么继续判断下一个字段 if ( name === "data" && jQuery.isEmptyObject( obj[name] ) ) { continue; } //如果有一个除了toJSON的字段,那么表示非空! if ( name !== "toJSON" ) { return false; } } return true; }
note:如果有一个除了toJSON的字段,那么表示这个数据对象不是空对象!
有了上面的18个问题,看jQuery.data或者jQuery._data的源码,或者实例data方法的源码应该不在话下了!源码如下:
jQuery.data方法源码
data: function( elem, name, data ) { return internalData( elem, name, data ); }
jQuery._data方法源码
_data: function( elem, name, data ) { return internalData( elem, name, data, true ); }
实例data方法源码
data: function( key, value ) { var i, name, data, //获取第1个调用元素 elem = this[0], //保存第一个调用元素的属性集合! attrs = elem && elem.attributes; // Special expections of .data basically thwart jQuery.access, // so implement the relevant behavior ourselves // Gets all values //没有传入key或者key为undefined if ( key === undefined ) { if ( this.length ) { //获取第1个元素的数据 data = jQuery.data( elem ); //如果第1个调用元素的parsedAttrs属性为空! if ( elem.nodeType === 1 && !jQuery._data( elem, "parsedAttrs" ) ) { i = attrs.length; while ( i-- ) { // Support: IE11+ // The attrs elements can be null (#14894) if ( attrs[ i ] ) { name = attrs[ i ].name; if ( name.indexOf( "data-" ) === 0 ) { name = jQuery.camelCase( name.slice(5) ); //通过dataAttr方法获取到HTML5数据 //传入如dataAttr(elem,"sex",undefined) dataAttr( elem, name, data[ name ] ); } } } jQuery._data( elem, "parsedAttrs", true ); } } return data; } // Sets multiple values if ( typeof key === "object" ) { return this.each(function() { jQuery.data( this, key ); }); } return arguments.length > 1 ? // Sets one value this.each(function() { jQuery.data( this, key, value ); }) : // Gets one value // Try to fetch any internally stored data first elem ? dataAttr( elem, key, jQuery.data( elem, key ) ) : undefined; }
internalRemoveData源码:
function internalData( elem, name, data, pvt /* Internal Use Only */ ) { if ( !jQuery.acceptData( elem ) ) { return; } var ret, thisCache, internalKey = jQuery.expando, // 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, // 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, // 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; // Avoid doing any more work than we need to when trying to get data on an // object that has no data at all if ( (!id || !cache[id] || (!pvt && !cache[id].data)) && data === undefined && typeof name === "string" ) { return; } if ( !id ) { // Only DOM nodes need a new unique ID for each element since their data // ends up in the global cache if ( isNode ) { id = elem[ internalKey ] = deletedIds.pop() || jQuery.guid++; } else { id = internalKey; } } if ( !cache[ id ] ) { // Avoid exposing jQuery metadata on plain JS objects when the object // is serialized using JSON.stringify cache[ id ] = isNode ? {} : { toJSON: jQuery.noop }; } // An object can be passed to jQuery.data instead of a key/value pair; this gets // shallow copied over onto the existing cache if ( typeof name === "object" || typeof name === "function" ) { if ( pvt ) { cache[ id ] = jQuery.extend( cache[ id ], name ); } else { cache[ id ].data = jQuery.extend( cache[ id ].data, name ); } } 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; } // Check for both converted-to-camel and non-converted data property names // If a data property was specified if ( typeof name === "string" ) { // First Try to find as-is property data ret = thisCache[ name ]; // Test for null|undefined property data if ( ret == null ) { // Try to find the camelCased property ret = thisCache[ jQuery.camelCase( name ) ]; } } else { ret = thisCache; } return ret; }
总结:
对于jQuery.data保存数据和获取数据严格遵守下面的次序:
第一步:获取expando以备用他来获取钥匙。expando=jQuery.expando;
第二步:通过expando获取到钥匙。JS对象:key=expando; DOM对象key=elem[expando]
第三步:获取仓库。JS对象walhouse=elem;DOM对象walhouse=jQuery.cache
第四步:用钥匙打开仓库。JS对象data=elem[expando],DOM对象data=walhouse[key]
第五步:获取数据,如果是用户数据要在data的data域下面,如果是内部数据不用到data域下面!
以上是深入解析jQuery.data,jQuery._data以及data實例函數的用法及注意事項的詳細內容。更多資訊請關注PHP中文網其他相關文章!

不同JavaScript引擎在解析和執行JavaScript代碼時,效果會有所不同,因為每個引擎的實現原理和優化策略各有差異。 1.詞法分析:將源碼轉換為詞法單元。 2.語法分析:生成抽象語法樹。 3.優化和編譯:通過JIT編譯器生成機器碼。 4.執行:運行機器碼。 V8引擎通過即時編譯和隱藏類優化,SpiderMonkey使用類型推斷系統,導致在相同代碼上的性能表現不同。

JavaScript在現實世界中的應用包括服務器端編程、移動應用開發和物聯網控制:1.通過Node.js實現服務器端編程,適用於高並發請求處理。 2.通過ReactNative進行移動應用開發,支持跨平台部署。 3.通過Johnny-Five庫用於物聯網設備控制,適用於硬件交互。

我使用您的日常技術工具構建了功能性的多租戶SaaS應用程序(一個Edtech應用程序),您可以做同樣的事情。 首先,什麼是多租戶SaaS應用程序? 多租戶SaaS應用程序可讓您從唱歌中為多個客戶提供服務

本文展示了與許可證確保的後端的前端集成,並使用Next.js構建功能性Edtech SaaS應用程序。 前端獲取用戶權限以控制UI的可見性並確保API要求遵守角色庫

JavaScript是現代Web開發的核心語言,因其多樣性和靈活性而廣泛應用。 1)前端開發:通過DOM操作和現代框架(如React、Vue.js、Angular)構建動態網頁和單頁面應用。 2)服務器端開發:Node.js利用非阻塞I/O模型處理高並發和實時應用。 3)移動和桌面應用開發:通過ReactNative和Electron實現跨平台開發,提高開發效率。

JavaScript的最新趨勢包括TypeScript的崛起、現代框架和庫的流行以及WebAssembly的應用。未來前景涵蓋更強大的類型系統、服務器端JavaScript的發展、人工智能和機器學習的擴展以及物聯網和邊緣計算的潛力。

JavaScript是現代Web開發的基石,它的主要功能包括事件驅動編程、動態內容生成和異步編程。 1)事件驅動編程允許網頁根據用戶操作動態變化。 2)動態內容生成使得頁面內容可以根據條件調整。 3)異步編程確保用戶界面不被阻塞。 JavaScript廣泛應用於網頁交互、單頁面應用和服務器端開發,極大地提升了用戶體驗和跨平台開發的靈活性。

Python更适合数据科学和机器学习,JavaScript更适合前端和全栈开发。1.Python以简洁语法和丰富库生态著称,适用于数据分析和Web开发。2.JavaScript是前端开发核心,Node.js支持服务器端编程,适用于全栈开发。


熱AI工具

Undresser.AI Undress
人工智慧驅動的應用程序,用於創建逼真的裸體照片

AI Clothes Remover
用於從照片中去除衣服的線上人工智慧工具。

Undress AI Tool
免費脫衣圖片

Clothoff.io
AI脫衣器

AI Hentai Generator
免費產生 AI 無盡。

熱門文章

熱工具

mPDF
mPDF是一個PHP庫,可以從UTF-8編碼的HTML產生PDF檔案。原作者Ian Back編寫mPDF以從他的網站上「即時」輸出PDF文件,並處理不同的語言。與原始腳本如HTML2FPDF相比,它的速度較慢,並且在使用Unicode字體時產生的檔案較大,但支援CSS樣式等,並進行了大量增強。支援幾乎所有語言,包括RTL(阿拉伯語和希伯來語)和CJK(中日韓)。支援嵌套的區塊級元素(如P、DIV),

SecLists
SecLists是最終安全測試人員的伙伴。它是一個包含各種類型清單的集合,這些清單在安全評估過程中經常使用,而且都在一個地方。 SecLists透過方便地提供安全測試人員可能需要的所有列表,幫助提高安全測試的效率和生產力。清單類型包括使用者名稱、密碼、URL、模糊測試有效載荷、敏感資料模式、Web shell等等。測試人員只需將此儲存庫拉到新的測試機上,他就可以存取所需的每種類型的清單。

EditPlus 中文破解版
體積小,語法高亮,不支援程式碼提示功能

SublimeText3 Linux新版
SublimeText3 Linux最新版

Dreamweaver Mac版
視覺化網頁開發工具