搜索
首页web前端js教程jQuery中数据缓存$.data的用法及源码完全解析_jquery

一、实现原理:

对于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>&#63;</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 &#63; 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 &#63; 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&#63; 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] &#63; "." + 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] &#63;
    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" &#63; true :
    data === "false" &#63; false :
    data === "null" &#63; null :
    jQuery.isNumeric( data ) &#63; parseFloat( data ) :
     rbrace.test( data ) &#63; 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 &#63; jQuery.cache : elem,

   // See jQuery.data for more information
   id = isNode &#63; 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 &#63; 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 &#63; 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 &#63; jQuery.cache[ elem[jQuery.expando] ] : elem[ jQuery.expando ];
   return !!elem && !isEmptyDataObject( elem );
   // 如果关联的数据缓存对象存在,并且含有数据,则返回true, 否则返回false。 这里用两个逻辑非运算符! 把变量elem转换为布尔值
 }
});

声明
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn
Java vs JavaScript:开发人员的详细比较Java vs JavaScript:开发人员的详细比较May 16, 2025 am 12:01 AM

javaandjavascriptaredistinctlanguages:javaisusedforenterpriseandmobileapps,while javascriptifforInteractiveWebpages.1)JavaisComcompoppored,statieldinglationallyTypted,statilly tater astrunsonjvm.2)

JavaScript数据类型:浏览器和nodejs之间是否有区别?JavaScript数据类型:浏览器和nodejs之间是否有区别?May 14, 2025 am 12:15 AM

JavaScript核心数据类型在浏览器和Node.js中一致,但处理方式和额外类型有所不同。1)全局对象在浏览器中为window,在Node.js中为global。2)Node.js独有Buffer对象,用于处理二进制数据。3)性能和时间处理在两者间也有差异,需根据环境调整代码。

JavaScript评论:使用//和 / * * / * / * /JavaScript评论:使用//和 / * * / * / * /May 13, 2025 pm 03:49 PM

JavaScriptusestwotypesofcomments:single-line(//)andmulti-line(//).1)Use//forquicknotesorsingle-lineexplanations.2)Use//forlongerexplanationsorcommentingoutblocksofcode.Commentsshouldexplainthe'why',notthe'what',andbeplacedabovetherelevantcodeforclari

Python vs. JavaScript:开发人员的比较分析Python vs. JavaScript:开发人员的比较分析May 09, 2025 am 12:22 AM

Python和JavaScript的主要区别在于类型系统和应用场景。1.Python使用动态类型,适合科学计算和数据分析。2.JavaScript采用弱类型,广泛用于前端和全栈开发。两者在异步编程和性能优化上各有优势,选择时应根据项目需求决定。

Python vs. JavaScript:选择合适的工具Python vs. JavaScript:选择合适的工具May 08, 2025 am 12:10 AM

选择Python还是JavaScript取决于项目类型:1)数据科学和自动化任务选择Python;2)前端和全栈开发选择JavaScript。Python因其在数据处理和自动化方面的强大库而备受青睐,而JavaScript则因其在网页交互和全栈开发中的优势而不可或缺。

Python和JavaScript:了解每个的优势Python和JavaScript:了解每个的优势May 06, 2025 am 12:15 AM

Python和JavaScript各有优势,选择取决于项目需求和个人偏好。1.Python易学,语法简洁,适用于数据科学和后端开发,但执行速度较慢。2.JavaScript在前端开发中无处不在,异步编程能力强,Node.js使其适用于全栈开发,但语法可能复杂且易出错。

JavaScript的核心:它是在C还是C上构建的?JavaScript的核心:它是在C还是C上构建的?May 05, 2025 am 12:07 AM

javascriptisnotbuiltoncorc; saninterpretedlanguagethatrunsonenginesoftenwritteninc.1)javascriptwasdesignedAsalightweight,解释edganguageforwebbrowsers.2)Enginesevolvedfromsimpleterterterpretpreterterterpretertestojitcompilerers,典型地提示。

JavaScript应用程序:从前端到后端JavaScript应用程序:从前端到后端May 04, 2025 am 12:12 AM

JavaScript可用于前端和后端开发。前端通过DOM操作增强用户体验,后端通过Node.js处理服务器任务。1.前端示例:改变网页文本内容。2.后端示例:创建Node.js服务器。

See all articles

热AI工具

Undresser.AI Undress

Undresser.AI Undress

人工智能驱动的应用程序,用于创建逼真的裸体照片

AI Clothes Remover

AI Clothes Remover

用于从照片中去除衣服的在线人工智能工具。

Undress AI Tool

Undress AI Tool

免费脱衣服图片

Clothoff.io

Clothoff.io

AI脱衣机

Video Face Swap

Video Face Swap

使用我们完全免费的人工智能换脸工具轻松在任何视频中换脸!

热工具

MinGW - 适用于 Windows 的极简 GNU

MinGW - 适用于 Windows 的极简 GNU

这个项目正在迁移到osdn.net/projects/mingw的过程中,你可以继续在那里关注我们。MinGW:GNU编译器集合(GCC)的本地Windows移植版本,可自由分发的导入库和用于构建本地Windows应用程序的头文件;包括对MSVC运行时的扩展,以支持C99功能。MinGW的所有软件都可以在64位Windows平台上运行。

SublimeText3 Mac版

SublimeText3 Mac版

神级代码编辑软件(SublimeText3)

SublimeText3 Linux新版

SublimeText3 Linux新版

SublimeText3 Linux最新版

EditPlus 中文破解版

EditPlus 中文破解版

体积小,语法高亮,不支持代码提示功能

DVWA

DVWA

Damn Vulnerable Web App (DVWA) 是一个PHP/MySQL的Web应用程序,非常容易受到攻击。它的主要目标是成为安全专业人员在合法环境中测试自己的技能和工具的辅助工具,帮助Web开发人员更好地理解保护Web应用程序的过程,并帮助教师/学生在课堂环境中教授/学习Web应用程序安全。DVWA的目标是通过简单直接的界面练习一些最常见的Web漏洞,难度各不相同。请注意,该软件中