jQuery的callbacks模块事实上是发布-订阅模式的一种实现,但可以通过以下参数对回调函数列表实现更多控制。包括:

once: 回调函数队列只能被执行一次(实现原理是判断回调函数队列是否被执行过,如果执行过,则不再执行)

unique: 回调函数不能重复添加到队列(实现原理是add的时候,判断现有队列是否已有相同函数,如果有则不再添加)

stopOnFalse: 回调函数队列中的函数是依次执行的,当某个函数的返回值为false的时候,停止后续函数执行(实现原理是fire的时候,如果某个函数返回值为true,则退出循环)

memory:当函数队列fire或者fireWith一次的时候(传递给fire的参数会传递给各个回调函数),内部会记录fire或者fireWith的参数,当下次add的时候,会将记录的参数传递给新添加的函数并立即执行这个新添加的函数(基本原理是使用memory参数记录每次fire时传递的参数,使用fireStart记录本次触发应当开始的下标)。

jQuery的Defered模式,就使用了once和memory的组合。进一步,我们知道Defered是ES6 promise的一种实现,可想promise也是基于类似的原理。

模块的具体用法可以参见这篇博客:https://segmentfault.com/a/1190000004331027

自己实现的callbacks模块相较于jquery源代码中的callbacks模块有所简化,表面上看没有考虑firing这个参数,没有对之进行任何处理,即没有考虑在函数执行过程中,再次调用add,remove,fire的种种情况。

话不多少,直接上代码,所有的思考都在代码里面:

这其中有几个关键的变量,控制回调函数列表执行的流程,细细体会其中的思想:

fired,

firingStart,

 var optionsCache={};
function createOptions(options){
var object=optionsCache[options]={};
//\S+,匹配非空格字符
//正则表达式如果没有g,仅匹配第一个匹配项
jQuery.each(options.match(/\S+/g),function(i,item){
object[item]=true;
});
return object;
}
//参数options可以是字符串或者是对象形式,可选属性/字符串组合有
//once:回调函数列表只能执行一次
//memory:fire调用之后,再次add将立即触发
//unique:同一个函数不能被重复添加到回调函数列表中
//stopOnFlase:当某一个函数返回false时,为true的时候,回调函数列表的执行终止
jQuery.callbacks=function(options){
options=typeof options==='string'?optionsCache[options]||createOptions(options):options||[];
var list=[],//用于存储回调函数列表
firingStart,
once=options.once,
memory,//初始值为undefined,只有在memory模式下,调用fire后才会被赋值,以备在add中再次调用
fired=false,//指示是否fire过
firingIndex,//指向要执行的下一个回调函数的下标
add=function(arg){
var type;
jQuery.each(arg,function(i,item){
type=jQuery.type(item);
if(type==='function'&&!(options.unique&&self.has(item))){
list.push(item);
}else if(type ==='array'){
add(item);
}
});
},
fire=function(data){
fired=true;
memory=options.memory&&data;
firingIndex=firingStart||;
firingStart=;//在memory模式下,add的时候firingStart可能会被置为其他值,这里将其还原,以备下次调用fire的时候从头开始执行
var length; if(!list){
return;
} for(length=list.length;firingIndex<length;firingIndex++){
if(list[firingIndex].apply(data[],data[])===false&&options.stopOnFalse){
break;
}
}
// if(once){
// if(memory){//如果通知是once和memory模式,那么在add的时候可以进行再次触发
// list=[];
// }else{//否则直接禁用
// self.disable();
// }
// } },
self={
add:function(){
if(list){
var start=list.length;
add(arguments);
//如果是memory模式下的add,会导致立即触发
if(memory){//memory的初始值为undefined,memory模式下调用一次fire才会被赋值,因此第一次调用add的时候不会走下面
firingStart=start;
fire(memory);
}
}
return this;
},
remove:function(){
if(list){
var i;
jQuery.each(arguments,function(i,item){
//jQuery.inArray(item,list,i),返回item在list中的下表,从第i位向后数,包括第i为
while((i=jQuery.inArray(item,list,i))>-){
list.splice(i,);//删除上的数值
}
});
}
return this;
},
//fn有值的时候,代表判断回调函数列表是否存在函数fn
//没有参数fn的时候,代表判断回调函数列表是否为空
has:function(fn){
return fn?jQuery.inArray(fn,list)>-:!!(list&&list.length);
},
empty:function(){
if(list){
list=[];
}
return this;
},
disable:function(){
//list就不用说了,list置为undefined之后,几乎所有的方法都不能调用
//memory恢复初始值undefined
list=memory=undefined;
return this;
},
disabled:function(){
return !list;
},
fireWith:function(context,args){
if(list&&!(once&&fired)){
args=args||[];//主要是为了处理args为undefined的情况
args=[context,args.slice?args.slice():args];
fire(args);
}
return this;
},
fire:function(){
self.fireWith(this,arguments);
return this;
},
fired:function(){
return !!fired;
}
};
return self;
};

截止目前,当前版本的myJquery.js的全部代码:

(function(window,undefined){
var rootjQuery,
core_version='2.0.3',
idExpr=/^#([\w\-]*)$/,
//下面两个正则用于转驼峰
rmsPrefix = /^-ms-/,
rdashAlpha = /-([\da-z])/gi,
rnotwhite = /\S+/g,//匹配非空白字符 class2type={},
core_deletedIds=[],
core_version='2.0.3', _jQuery=window.jQuery,
_$=window.$, core_toString=class2type.toString,
core_hasOwn=class2type.hasOwnProperty,
core_trim=core_version.trim,
core_indexOf=core_deletedIds.indexOf,
core_push=core_deletedIds.push,
core_concat=core_deletedIds.concat,
core_slice=core_deletedIds.slice, //用于jQuery.camelCase转驼峰函数中
//当replace函数只有一个匹配项时,第二个参数可以是一个函数
//如果repalce中的正则没有捕获组,会向这个函数传递三个参数:模式的匹配项,模式匹配项在字符串中的位置,原始字符串
//如果replace中的正则有捕获组,也会向这个函数传递三个参数,模式的匹配项,捕获组的匹配项,模式匹配项在字符串中的位置
fcamelCase=function(all,letter){
return letter.toUpperCase();
},
jQuery=function(selector,context){
return new jQuery.fn.init(selector,context,rootjQuery);
}; //jQuery相关实例方法和属性
jQuery.fn=jQuery.prototype={
jQuery:core_version,//其实就是版本字符串2.0.3
constructor:jQuery,//还原constructor指向
selector:'',//含有连续的整型属性、length属性、context属性,selector属性(在jQuery.fn.init中设置),preObject属性(在pushStack中设置)
length:,
init:function(selector,context,rootjQuery){
var match,elem;
//selector是选择器表达式
if(!selector){
return this;
} if(typeof selector ==='string'){
match=idExpr.exec(selector);
if(match&&!context){
elem=document.getElementById(match[]);
if(elem&&elem.parentNode){
this[]=elem;
this.length=;
}
this.selector=selector;
this.context=document;
return this;
}else{
//说明是复杂的选择器表达式,这里暂不考虑
}
}
//处理selector是DOM元素的情形
if(selector&&selector.nodeType){
this[]=selector;
this.length=;
this.context=selector;
return this;
}
//处理selector是函数的情形
if(jQuery.isFunction(selector)){
return rootjQuery.ready( selector );
}
//处理selector是jQuery对象的情形
if(selector.selector){
this.selector=selector.selector;
this.context=selector.context;
}
//处理其他情形
return jQuery.makeArray(selector,this); },
//将jQuery类数组对象转换为数组
toArray:function(){
return core_slice.call(this);
},
//如果传递了参数num,代表获取下标num的DOM元素(num可以为负数)
//如果没有传递num,则将jQuery对象转换为数组后整体返回
get:function(num){
if(num==null){//注意这里不能用!num,因为num可以为0
return this.toArray();
}
return num<?this[num+this.length]:this[num];
},
//入栈
pushStack:function(elems){ var ret=jQuery.merge(this.constructor(),elems); ret.prevObject=this;
ret.context=this.context;
return ret;
},
//遍历jQuery对象
each:function(callback,args){
//在静态方法已经指定了callback的执行上下文
return jQuery.each(this,callback,args);
},
//加载完成事件方法,这里暂不考虑
ready:function(fn){},
slice:function(){
//注意apply和call的区别
return this.pushStack(core_slice.apply(this,arguments));
},
first:function(){
return this.get();
},
last:function(){
return this.get(-);
},
eq:function(i){
var length=this.length,
j=+i+(i<?length:);
return this.pushStack(j>=&&j<length?[this[j]]:[]);
},
map:function(callback){
//这种写法不能指定callback的执行环境,因为在静态方法jQuery.map并没有指定callback的执行上下文
// return this.pushStack(jQuery.map(this,callback));
return this.pushStack(jQuery.map(this,function(elem,i){
return callback.call(elem,i,elem);
}));
},
//与pushStack方法相对应,返回栈的上一级
end:function(){
return this.prevObject||this.constructor();
},
push:core_push,
sort:[].sort,
splice:[].splice,
};
jQuery.fn.init.prototype=jQuery.fn; //可接受的参数类型如下:jQuery.extend([deep],target,object1,[objectN])
jQuery.extend=jQuery.fn.extend=function(){
var target=arguments[]||{},//指向目标对象
deep=false,//是否进行深度复制
i=,//表示源对象的起始下标
length=arguments.length,//表示参数个数;
options,name,src,copy,copyIsArray;//options指向某个源对象,name指向源对象的某个属性名,src目标对象某个属性的原始值,copy某个源对象的某个属性的值,copyIsArray指示变量copy是否为数组
//首先进行参数修正
if(typeof target==='boolean'){
deep=target;
target=arguments[]||{};
i=;
}
//此时target就是jQuery或jQuery.fn
if(i===length){
target=this;
i--;
}
//处理target是字符串或者其他情形,这在深度复制中可能出现
// if(typeof target!=='object'||!jQuery.isFunction(target)){
// target={};
// }
for(i;i<length;i++){
options=arguments[i];
for(name in options){
src=target[name];
copy=options[name];
if(deep&&copy&&(jQuery.isPlainObject(object)||(copyIsArray=jQuery.isArray(object)))){
if(copyIsArray){
copyIsArray=false;
clone=src&&jQuery.isArray(src)?src:[];
}else{
clone=src&&jQuery.isPlainObject(src)?src:{};
}
target[name]=jQuery.extend(deep,clone,copy);
}else{
target[name]=copy;
}
}
}
return target;
};
//检查是否是数组或者类数组
function isArrayLike(obj){
var length=obj.length,
type=jQuery.type(obj);
if(obj&&jQuery.isWindow(obj)){
return false;
}
if(obj.nodeType===&&length){
return true;
} if(type==='array'){
return true;
}
if(typeof length==='number'&&(length==||(length>&&(length-) in obj))){
return true;
}
return false;
}
jQuery.extend({
//一堆静态方法和属性
expando:'jQuery'+(core_version+Math.random()).replace(/\D/g,''),
// 该函数用于释放jQuery对于全局变量$的控制权,可选的参数deep代表是否释放对全局变量jQuery的控制权
noConflict:function(deep){
if(window.$===jQuery){
window.$=_$;
}
if(deep&&window.jQuery===jQuery){
window.jQuery=_jQuery;
}
return jQuery;
},
/********isReady,readyWait,holdReay,ready与加载事件有关,暂且略过***********/
isReady:false,
readyWait:,
holdReady:function(hold){},
ready:function(){},
/*******/ /****下面是一系列类型检测的静态方法*******/
isFunction:function(obj){
//如果使用typeof,在有些浏览器中,正则也会返回function,因此这里采用jQuery处理后的方法,jQuery.type
return jQuery.type(obj)==='function';
},
isArray:Array.isArray,
isWindow:function(obj){
return obj!==null&&obj===obj.window;
},
//判断obj是否为数字或者数字类型的字符串,并且是有效数字
isNumeric:function(obj){
return !isNaN(parseFloat(obj))&&isFinite(obj);
},
type:function(obj){
if(obj===null){
return String(null);
}
//Date,Array等类型typeof都会返回object,function、正则(部分浏览器)中 typeof都会返回function if(typeof obj==='object'||typeof obj==='function'){
return class2type[core_toString.call(obj)]||'object';
}
return typeof obj;
},
//判断是否为以下两种情况:1,对象字面量;2,通过new Object()创建
isPlainObject:function(obj){
if(jQuery.type(obj)!=='object'||obj.nodeType||jQuery.isWindow(obj)){
return false;
} //如果是纯粹的对象,那么obj一定有constructor属性,并且方法hasOwnPropertyOf一定就在构造函数本身的原型中,而不用通过原型链查找得到
if(obj.constructor&&!core_hasOwn.call(obj.constructor.prototype,'isPrototypeOf')){
return false;
}
return true; },
//检查是否是空对象
isEmptyObject:function(obj){
for(var name in obj){
return false;
}
return true;
},
/******类型检测静态方法结束********/ error:function(msg){
throw new Error(msg);
},
//将html字符串转换为html DOM结构,
parseHTML: function( data, context, keepScripts ){ },
parseJSON:JSON.parse,
parseXML:function(data){
var xml, tmp;
if ( !data || typeof data !== "string" ) {
return null;
} // Support: IE9
try {
tmp = new DOMParser();
xml = tmp.parseFromString( data , "text/xml" );
} catch ( e ) {
xml = undefined;
} if ( !xml || xml.getElementsByTagName( "parsererror" ).length ) {
jQuery.error( "Invalid XML: " + data );
}
return xml;
},
noop:function(){},
//用于在全局作用域执行javascript代码,这里暂略
globalEval:function(data){},
//转换连字符字符串为驼峰类型
camelCase:function(string){
return string.replace(rmsPrefix,'ms-').replace(rdashAlpha,fcamelCase);
},
//判断elem的nodeName是否=name
nodeName:function(elem,name){
return elem.nodeName&&elem.nodeName.toLowerCase()==name.toLowerCase();
},
//jQuery遍历方法,其中args是传递给回调callback的参数,仅供jQuery内部使用;外部调用该方法时,回调的参数默认为数组下标/对象key,对应数组值/对象value
each:function(object,callback,args){
var i,
value,
length=object.length,
isArray=isArrayLike(object); if(args){//说明是内部调用
if(isArray){
for(i=;i<length;i++){
value= callback.call(object[i],args);
if(value===false){
break;
}
}
}else{
for(i in object){
value=callback.call(object[i],args);
if(value===false){
break;
}
}
}
}else{
if(isArray){
for(i=;i<length;i++){
value=callback.call(object[i],i,object[i]);
if(value===false){
break;
}
}
}else{
for(i in object){
value=callback.call(object[i],i,object[i]);
if(value===false){
break;
}
}
}
}
return object;
},
trim:function(str){
return str==null?'':core_trim.call(str);
},
//将一个类数组对象转换为真正的对象
//results参数仅供jquery内部使用,此时在该参数的基础上添加元素
makeArray:function(array,results){
var ret=results||[],
type=jQuery.type(array);
//undefined,null都会==null
if(array!=null){
//1,没有length属性,或者具有length属性,但是是以下几种情况的
//2.如果array是string 的length表示字符串的长度
//3.如果array是函数,其length代表函数生命时的参数个数
//4,如果array是window对象,属性Length返回窗口中的框架(frame,iframe)个数
if(array.length==null|| type=='string' || type=='function' ||type=='regexp'||jQuery.isWindow(array)){
core_push.call(ret,array);
}else{//否则说明是类数组对象
jQuery.merge(ret,array);
}
}
return ret;
}, inArray:function(elem,array,i){
return array==null?-:core_indexOf.call(array,elem,i);
},
//用于合并两个数组的元素到第一个数组中
//事实上,jquery源代码中第一个参数可以是数组或者类数组对象,第二个参数可以是数组、类数组对象或任何含有连续整型属性的对象
//第一个参数是数组,最后返回数组;第一个参数是类数组,则返回类数组
merge:function(first,second){
var l=second.length,
i=first.length,
j;
if(typeof l=='number'){
for(j=;j<l;j++){
first[i++]=second[j];
}
}else{
while(second[j]!=undefined){
first[i++]=second[j++];
}
} first.length=i;
return first;
},
//用于查找数组中满足过滤函数的元素,形成新的数组之后返回,原数组不受影响
//如果inv未传入或者是false,元素只有在过滤函数返回true时,才会被保存在最终的结果数组中
//如果参数inv是true,则恰好相反
grep:function(elems,callback,inv){
var i,
ret=[],
length=elems.length,
retVal;
inv=!!inv;
for(i=;i<length;i++){
retVal=!!callback.call(elems[i],i);
if(retVal!==inv){
ret.push(elems[i]);
}
}
return ret;
},
//用于对数组中每个元素执行callback操作,并将结果形成新的数组返回
//参数arg仅仅是jQuery内部使用
map:function(elems,callback,arg){
var ret=[],
retVal,
i,
length=elems.length,
isArray=isArrayLike(elems);
if(isArray){
for(i=;i<length;i++){
retVal=callback(elems[i],i,arg);//注意不是callback.call
if(retVal!=null){
ret.push(retVal);
}
}
}else{
for(i in elems){
retVal=callback(elems[i],i,arg);
if(retVal!=null){
ret.push(retVal);
}
}
}
//保证最终返回的是一维数组
return core_concat.call([],ret);
},
guid:,
//该方法用于更改函数的执行上下文
//源代码中有两种传参形式,这里仅考虑最常见的一种
proxy:function(fn,context){
if(!jQuery.isFunction(fn)){
return undefined;
}
var args=core_slice.call(arguments,);
proxy=function(){
return fn.call(context||this,core_concat.call(args,core_slice.call(arguments)));
};
proxy.guid=fn.guid=fn.guid||jQuery.guid++;
return proxy;
},
//用一个方法同时实现get和set操作
//如何设置或者获取由回调函数fn确定
//这个方法的实现等用到的时候结合来看
access: function( elems, fn, key, value, chainable, emptyGet, raw ){ },
now:Date.now,
//该方法用于交换css样式,在support模块较多用到
//要交换的样式由参数options传递
swap: function( elem, options, callback, args ){
var name,ret,
old={};
for(name in options){
old[name]=elem.style[name];
elem.style[name]=options[name];
}
ret=callback.call(elem,args||[]);
for(name in options){
elem.style[name]=old[name];
}
return ret;
}, }); //目前,js中typeof的返回值有六种:"number," "string," "boolean," "object," "function," 和 "undefined."
//通过object.prototype.toString/或者{}.toString 返回值有九种:Boolean Number String Function Array Date RegExp Object Error,其中的Array,Date,RegExp,Object,Error都属于Object类型,在有些浏览器中typeof 正则会返回function
jQuery.each("Boolean Number String Function Array Date RegExp Object Error".split(" "),function(i,name){
class2type["[object "+name+"]"]=name.toLowerCase();
});
//console.log(class2type,class2type);
rootjQuery=jQuery(document); /****接下来这一部分,在jQuery的源代码中,本来是Sizzle,这里暂且略过***/
var optionsCache={};
function createOptions(options){
var object=optionsCache[options]={};
//\S+,匹配非空格字符
//正则表达式如果没有g,仅匹配第一个匹配项
jQuery.each(options.match(/\S+/g),function(i,item){
object[item]=true;
});
return object;
}
//参数options可以是字符串或者是对象形式,可选属性/字符串组合有
//once:回调函数列表只能执行一次
//memory:fire调用之后,再次add将立即触发
//unique:同一个函数不能被重复添加到回调函数列表中
//stopOnFlase:当某一个函数返回false时,为true的时候,回调函数列表的执行终止
jQuery.callbacks=function(options){
options=typeof options==='string'?optionsCache[options]||createOptions(options):options||[];
var list=[],//用于存储回调函数列表
firingStart,
once=options.once,
memory,//初始值为undefined,只有在memory模式下,调用fire后才会被赋值,以备在add中再次调用
fired=false,//指示是否fire过
firingIndex,//指向要执行的下一个回调函数的下标
add=function(arg){
var type;
jQuery.each(arg,function(i,item){
type=jQuery.type(item);
if(type==='function'&&!(options.unique&&self.has(item))){
list.push(item);
}else if(type ==='array'){
add(item);
}
});
},
fire=function(data){
fired=true;
memory=options.memory&&data;
firingIndex=firingStart||;
firingStart=;//在memory模式下,add的时候firingStart可能会被置为其他值,这里将其还原,以备下次调用fire的时候从头开始执行
var length; if(!list){
return;
} for(length=list.length;firingIndex<length;firingIndex++){
if(list[firingIndex].apply(data[],data[])===false&&options.stopOnFalse){
break;
}
}
// if(once){
// if(memory){//如果通知是once和memory模式,那么在add的时候可以进行再次触发
// list=[];
// }else{//否则直接禁用
// self.disable();
// }
// } },
self={
add:function(){
if(list){
var start=list.length;
add(arguments);
//如果是memory模式下的add,会导致立即触发
if(memory){//memory的初始值为undefined,memory模式下调用一次fire才会被赋值,因此第一次调用add的时候不会走下面
firingStart=start;
fire(memory);
}
}
return this;
},
remove:function(){
if(list){
var i;
jQuery.each(arguments,function(i,item){
//jQuery.inArray(item,list,i),返回item在list中的下表,从第i位向后数,包括第i为
while((i=jQuery.inArray(item,list,i))>-){
list.splice(i,);//删除上的数值
}
});
}
return this;
},
//fn有值的时候,代表判断回调函数列表是否存在函数fn
//没有参数fn的时候,代表判断回调函数列表是否为空
has:function(fn){
return fn?jQuery.inArray(fn,list)>-:!!(list&&list.length);
},
empty:function(){
if(list){
list=[];
}
return this;
},
disable:function(){
//list就不用说了,list置为undefined之后,几乎所有的方法都不能调用
//memory恢复初始值undefined
list=memory=undefined;
return this;
},
disabled:function(){
return !list;
},
fireWith:function(context,args){
if(list&&!(once&&fired)){
args=args||[];//主要是为了处理args为undefined的情况
args=[context,args.slice?args.slice():args];
fire(args);
}
return this;
},
fire:function(){
self.fireWith(this,arguments);
return this;
},
fired:function(){
return !!fired;
}
};
return self;
}; window.jQuery=window.$=jQuery;
})(window);

myJquery1019.js

jQuery源代码学习之四——jQuery.callbacks的更多相关文章

  1. jQuery源代码学习之八——jQuery属性操作模块

    一.jQuery属性模块整体介绍 jQuery的属性操作模块分四个部分:html属性操作,dom属性操作,类样式操作,和值操作. html属性操作(setAttribute/getAttribute) ...

  2. jQuery源代码学习之六——jQuery数据缓存Data

    一.jQuery数据缓存基本原理 jQuery数据缓存就两个全局Data对象,data_user以及data_priv; 这两个对象分别用于缓存用户自定义数据和内部数据: 以data_user为例,所 ...

  3. jQuery源代码学习之五——jQuery.when

    jQuery.when提供了基于一个或多个对象的状态来执行回调函数的功能,通常是基于具有异步事件的异步队列. 如果传入多个异步队列,jQuery.when会返回一个新的主异步队列的只读副本(promi ...

  4. jQuery源代码学习_工具函数_type

    jquery源代码学习_工具函数_type jquery里面有一个很重要的工具函数,$.type函数用来判断类型,今天写这篇文章,是来回顾type函数的设计思想,深入理解. 首先来看一下最终结果: 上 ...

  5. jQuery源代码学习笔记_工具函数_noop/error/now/trim

    jQuery源代码学习笔记_工具函数_noop/error/now/trim jquery提供了一系列的工具函数,用于支持其运行,今天主要分析noop/error/now/trim这4个函数: 1.n ...

  6. jQuery源代码学习之九—jQuery事件模块

    jQuery事件系统并没有将事件坚挺函数直接绑定在DOM元素上,而是基于事件缓存模块来管理监听函数的. 二.jQuery事件模块的代码结构 //定义了一些正则 // // //jQuery事件对象 j ...

  7. jQuery源代码学习之七—队列模块queue

    一.jQuery种的队列模块 jQuery的队列模块主要是为动画模块EFFECTS提供支持,(不过到现在为了支持动画队列的inprogress的出入队还是搞不太清楚),单独抽取出一个命名空间是为了使程 ...

  8. javascript源代码学习之五——jQuery.deferred

    jQuery.Defered——异步队列用于管理一组回调函数(成功resolve,失败reject,消息progress),基于上一节实现的jQuery.callbacks完成. done,fail, ...

  9. jQuery源代码学习笔记:构造jQuery对象

    2.1源代码结构: (function( window, undefined ) { var jQuery = (function() { // 构建jQuery对象 var jQuery = fun ...

随机推荐

  1. POJ 2456 (二分)

    题目链接: http://poj.org/problem?id=2456 题目大意:n个房子,m头牛,房子有一个横坐标,问将m头牛塞进房子,每两头牛之间的最大间隔是多少. 解题思路: 不难看出应该二分 ...

  2. How to crack gbooks

    Damn cnblogs, no auto saving set by default, even worse than csdn, can't believe it, lost half an ho ...

  3. OpenResty 通过二级域名做跳转

    if ( $host ~* (\b(?!www\b)\w+)\.\w+\.\w+ ) { #获取nba.test.com域名中的nba set $subdomain $1; } location / ...

  4. xenu工具介绍

    Xenu Link Sleuth 也许是你所见过的最小但功能最强大的检查网站死链接的软件了.你可以打开一个本地网页文件来检查它的链接,也可以输入任何网址来检查.它可以分别列出网站 的活链接以及死链接, ...

  5. [CareerCup] 17.6 Sort Array 排列数组

    17.6 Given an array of integers, write a method to find indices m and n such that if you sorted elem ...

  6. visual studio 2012 的制作ActiveX、打包和发布[转]

    开发环境是Vs 2012  Framework 4.0 源地址:http://www.cnblogs.com/li-peng/p/3455247.html 源码和制作工具在文章最下边 一. Activ ...

  7. C# 文件和文件夹操作

    一.文件操作 1.File类的常用静态方法: void AppendAllText(string path, string contents),将文本contents附加到文件path中 bool E ...

  8. 温故而知新,jquery选择器$=

    $("select[name$=Sort]") 可以选择到如下元素: <select class="sort-selec" name="eton ...

  9. js控制只允许输入数字

    avascript 只允许输入数字有很多方法,总结如下 1,只允许输入数字和小数点. <input onKeypress="return (/[\d.]/.test(String.fr ...

  10. JDBC使用MySQL存储过程错误

    JDBC连接执行 MySQL 存储过程报权限错误:User does not have access to metadata required to determine stored procedur ...