最原始的事件注册

addEventListener方法大家应该都很熟悉,它是Html元素注册事件最原始的方法。先看下addEventListener方法签名:

element.addEventListener(event, function, useCapture)

event:事件名,例如“click”,这里要提醒的一点是不要加前缀“on”;
    function:事件触发时执行的函数;
    userCapture:默认为false,表示event事件在冒泡阶段触发。如果设置为true,则事件将会在捕获阶段触发。如果不清楚什么是捕获和冒泡,请自觉了解事件的冒泡机制(友情链接:勤能补挫-简单But易错的JS&CSS问题总结)。
    虽然addEventListener包含了三个参数,但一般我们都只使用了前两个参数,下面的代码只使用了两个参数:

document.getElementById("myBtn").addEventListener("click", function() {
alert(“我是在冒泡阶段触发的哦!”);
});

上面代码注册的函数会在冒泡阶段触发,如果想在捕获阶段触发,直接把第三个参数传递进去就ok了。在实现DOM元素拖拽功能时,会使用到捕获方式。

另外,IE8以及之前的版本不支持事件按捕获形式传播,并且注册方法也没有addEventListener函数,IE为事件注册提供了attachEvent方法。和addEventListener相似,也包含有event和function参数,但不包含第三个参数。

jQuery事件注册

jQuery的事件函数通过jQuery.fn.extend附加到jQuery对象,jQuery.fn.extend包含了jQuery的所有事件注册函数。那么jQuery到底提供了哪些事件函数?这里把这些函数分层了三类:

(1)和事件同名的函数:jQuery几乎提供了所有DOM元素事件的同名函数,像我们经常使用的click、focus、scroll等函数。使用也很简单,例如我们要给div元素绑定click事件,可以直接写成$(“div”).click(function(){})。DOM元素的事件有很多,jQuery为每个事件都添加了同名的注册函数吗?看源码!

//循环遍历所有的dom元素事件名数组
jQuery.each( ("blur focus focusin focusout load resize scroll unload click dblclick " +
"mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave " +
"change select submit keydown keypress keyup error contextmenu").split(" "), function( i, name ) {
//把dom元素所有事件通过fn[事件名]的方式添加到jquery对象
// Handle event binding
jQuery.fn[ name ] = function( data, fn ) {
//如果参数长度大于0,则调用on方法委托函数到name事件;如果参数长为0,则触发事件执行
return arguments.length > 0 ?
this.on( name, null, data, fn ) :
this.trigger( name );
};
});

首先看到的是一串包含了所有DOM元素事件的字符串,通过空格把字符串分隔成数组。如果传递的参数长度大于0,则调用jQuery对象的on方法注册事件。如果参数长度为0,则直接调用trigger方法触发事件。例如(“div”).click(function())将会调用on方法注册事件,而(“div”).click()则调用trigger方法,立即触发click事件。
    上面的代码有几点需要作下解释:
    jQuery.fn中的函数包含的上下文this是指向jQuery实体,例如$(“div”)实体。
    jQuery.fn[name] = function(){}等效于jQuery.fn.name = function(){},例如jQuery.fn[“click”] = function(){}等效于jQuery.fn.click = function(){}。
    This.on和this.trigger方法这里暂不忙解释。

(2)绑定和委托函数:bind/unbind和delegate/undelegate方法通过jQuery.fn.extend附加到jQuery对象上。代码很简单:

jQuery.fn.extend({
//事件绑定
bind: function( types, data, fn ) {
return this.on( types, null, data, fn );
},
//事件解绑
unbind: function( types, fn ) {
return this.off( types, null, fn );
},
//事件委托
delegate: function( selector, types, data, fn ) {
return this.on( types, selector, data, fn );
},
//委托解绑
undelegate: function( selector, types, fn ) {
return arguments.length === 1 ? this.off( selector, "**" ) : this.off( types, selector || "**", fn );
}
});

bind和delegate都是直接调用jQuery对象的on函数,唯一区别是传递的参数不同,bind的第二个参数为null,而委托的第二个参数是一个selector。别小看这个区别,使用jQuery绑定事件常出的问题部分原因就是没搞清楚这两个参数的区别。

(3)底层注册函数:前面介绍的和事件同名的函数、绑定和委托函数最终都是调用了jQuery对象的on函数,我们在编程的时候也可以直接使用on函数。on函数代码比较复杂,我们先看看外壳:

jQuery.fn.extend({
//比较底层的事件委托函数,其他函数都是调用这个来和元素建立绑定或者委托
on: function( types, selector, data, fn, /*INTERNAL*/ one ) {
return this.each( function() {
jQuery.event.add( this, types, fn, data, selector );
});
},
//一次性事件绑定
one: function( types, selector, data, fn ) {
return this.on( types, selector, data, fn, 1 );
},
//比较底层的事件解绑,其他解绑函数都是调用该函数执行解绑
off: function( types, selector, fn ) {
return this.each(function() {
jQuery.event.remove( this, types, fn, selector );
});
},
//触发事件
trigger: function( type, data ) {
return this.each(function() {
jQuery.event.trigger( type, data, this );
});
},
//只执行元素绑定的处理函数,不会触发浏览器的默认动作
triggerHandler: function( type, data ) {
var elem = this[0];
if ( elem ) {
return jQuery.event.trigger( type, data, elem, true );
}
}
});

为什么说是底层的函数?因为前面的所有绑定最终都是调用on函数,所有的解绑最终调用off函数。这里还包含了trigger和triggerHandler函数,前一个是触发元素的所有type事件行为,而triggerHandler只触发绑定的函数而不触发行为。例如focus事件,triggerHandler只会触发绑定给元素的focus处理函数,而不会真的让元素获得焦点。但trigger函数会让元素获取焦点。

汇总一下,jQuery提供的事件处理函数不外乎也就下面这些。

委托还是绑定?

这里为什么提出了委托和绑定?事出有因,我们慢慢来分析。之前介绍了几类事件绑定,先分下类便于后面的分析。以什么分类?就以调用on函数的第二个参数为不为null。

(1)为null的一类on(types, null, data, fn)事件

bind、blur、focus、focusin、focusout、load、resize、scroll、unload、click、dblclick、mousedown、mouseup、mousemove、mouseover、mouseout、mouseenter、mouseleave、 change、select、submit、keydown、keypress、keyup error、contextmenu。

(2)不为null的一类on(types, selector, data, fn)事件

delegate、on

接下来我们举一个场景:给div容器(class为parent)列表中的每一项(class为child)添加click事件,并且列表的项可动态添加。

<div class="parent">
<div class="child">第1个儿子</div>
<div class="child">第2个儿子</div>
<div class="child">第3个儿子</div>
</div>
<button id="btn">生儿子</button>
<script type="text/javascript">
var i = 4;
(".parent.child").click(function(){alert("我是你儿子"});
//(".parent.child").click(function(){alert("我是你儿子"});
//(".parent .child").bind("click", function(){
// alert("我是你儿子");
// })
("#btn").click(function(){
$(".parent").append("<div class='child'>第" + (i++) + "个儿子</div>");
});
</script>

页面加载后点击前三个儿子都会提示“我是你儿子”,现在我点击btn按钮,添加第四个儿子,然后再点击新增项看看。发现没有再弹出提示信息。上面代码注册事件使用的是click或者bind函数,效果都是一样:动态添加的子项没有触发事件了。其实,“为null的一类”事件效果都是这样。现在我们再把事件绑定改成delegate或者on函数:

//(".parent").on("click", ".child", function(){
// alert("我是你儿子");
// });
$(".parent").delegate(".child", "click", function(){
alert("我是你儿子");
});

测试结果发现,不管是on或者delegate,我们后面动态添加的子项都能触发事件。

通过上面的场景不难看出,click和bind函数只支持静态绑定,只能绑定给已经有的节点,后期动态生成的节点不支持。这样的行为我们可称为“绑定”。而通过delegate或者on方法通过传递一个selector,把通过selector筛选的元素的事件全权“委托”给父容器。所以事件其实是绑定在父容器上,只是在处理事件时jQuery内部做了委托处理。
    那么,到底是委托好还是绑定好?个人建议如果筛选的元素比较少,可以使用click或者bind,比较简单并且代码也容易理解。但如果筛选出的元素可能包含成百上千,那么肯定使用delegate或者on,这样性能比bind高多了。delegate、on事件只会绑定给父容器,即使1000个节点,还是只绑定一次。而bind的话就得乖乖的绑定1000次。
不管是委托还是绑定,都是通过on注册。所以搞清楚on函数的实现也就搞清楚了jQuery的事件机制。

jQuery源代码分析

jQuery.fn.on函数

既然绑定和委托最终都是调用on函数,那么只要把on方法代码流程了解清楚,整个事件绑定机制也了解的差不多。On函数代码其实比较简单,包含参数处理和事件添加两个部分。函数包含了5个参数:

on: function( types, selector, data, fn, /*INTERNAL*/ one )

但是我们经常使用on函数并没有传递这么多参数,而是像这样:

(“a”).on(“click”,function());(“a”).on(“click”,function());(“a”).on(“click”, “p”, function(){});
(“a”).on(“click,mouseover,focus”,function());
(“a”).on(“click,mouseover,focus”,function());
(“”).on(“click”, {id: 1, name: “test”}, function{});

on函数大部分代码都是处理传入的参数,最后三行代码使用each遍历jQuery对象中的元素并调用jQuery.event.add方法。源代码如下:

<DIV class=cnblogs_code
style="BORDER-TOP: #cccccc 1px solid; BORDER-RIGHT: #cccccc 1px solid; BORDER-BOTTOM: #cccccc 1px solid; PADDING-BOTTOM: 5px; PADDING-TOP: 5px; PADDING-LEFT: 5px; BORDER-LEFT: #cccccc 1px solid; PADDING-RIGHT: 5px; BACKGROUND-COLOR: #f5f5f5"><PRE><SPAN style="COLOR: #000000">jQuery.fn.extend({
//比较底层的事件委托函数,其他函数都是调用这个来和元素建立绑定或者委托
on: function( types, selector, data, fn, /*INTERNAL*/ one ) {
var origFn, type;
//参数为types/handlers,("click", function)
if ( typeof types === "object" ) {
// ( types-Object, selector, data )。例如({'click': function1,'focus': function2}, selector, data)
if ( typeof selector !== "string" ) {
// ( types-Object, data )。例如({'click': function1,'focus': function2}, data)
data = data || selector;
selector = undefined;
}
//遍历{'click': function1,'focus': function2}
for ( type in types ) {
//每个type再单独调用on注册一次
this.on( type, selector, data, types[ type ], one );
}
return this;
}
//只有两个参数,{types,fn}
if ( data == null &amp;&amp; fn == null ) {
// ( types, fn )
fn = selector;
data = selector = undefined;
}
//fn == null &amp;&amp; data != null,只有三个参数的情况
else if ( fn == null ) {
if ( typeof selector === "string" ) {
// ( types, selector, fn ),例如:("click", "a,p", function(){})
fn = data;
data = undefined;
} else {
// ( types, data, fn ), 例如:("click", {id: 1, name: "test"}, function(e){})
fn = data;
data = selector;
selector = undefined;
}
}
if ( fn === false ) { //如果fn等于false,重新赋给fn一个return false的函数。
fn = returnFalse;
} else if ( !fn ) { //如果fn未定义或者为null,不做任何操作,直接返回链式对象this
return this;
} if ( one === 1 ) { //事件只执行一次
origFn = fn;
fn = function( event ) { //重写fn函数,在执行fn函数一次后,注销事件
// 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++ ); //赋值fn.guid等于原始函数origFn.guid
}
//jQuery对象包含的元素是一个集合,所以需要遍历每个元素执行event.add
return this.each( function() {
//event.add做了什么操作?
jQuery.event.add( this, types, fn, data, selector );
});
}
}

jQuery.event对象

jQuery.fn.on函数最后三行代码调用了jQuery.event.add函数,add是jQuery.event的一个函数。在了解add之前先看看jQuery.event,jQuery.event究竟包含哪些东西:

jQuery.event = {
//函数,为元素添加事件
add: function( elem, types, handler, data, selector ) {},
//函数,为元素删除事件
remove: function( elem, types, handler, selector, mappedTypes ) {},
//函数,触发元素事件
trigger: function( event, data, elem, onlyHandlers ) {},
//函数,执行元素事件
dispatch: function( event ) {},
//函数,事件队列
handlers: function( event, handlers ) {},
//属性,KeyEvent和MouseEvent事件属性
props: "altKey bubbles cancelable ctrlKey currentTarget eventPhase metaKey relatedTarget shiftKey target timeStamp view which".split(" "),
//函数,扩展event。添加一些附加属性,像target、type、origainEvent等属性
fix: function( event ) {},
//对象,特殊事件
special: {},
//函数,模拟事件行为,例如focus、unfocus行为
simulate: function( type, elem, event, bubble ) {}
}

现在我们想要搞清楚的是jQuery怎样添加事件,以及如何执行事件。要了解清楚这些问题,就必须得搞清楚代码中的add、dispatch、handlers三个函数。

为了容易理解这些函数的关系,下面是一个函数执行顺序的流程图:

jQuery.event.add函数

事件是建立在DOM元素之上,DOM元素和事件要建立关系,最原始的方法是在DOM元素上绑定事件。jQuery为了不破坏DOM树结构,通过缓存的方式保存事件。jQuery内部有一个叫做Data的缓存对象,通过key/value这种方式缓存数据。细心的同学在使用jQuery时会发现DOM元素多了一个以jQuery开头的属性,例如jQuery20303812802915245450.4513941336609537:3。这个属性正是jQuery缓存的key值。
    Add函数中的elemData就是一个类型为Data的缓存对象,在调用get时需要把元素作为参数传递进去, 查找元素的属性以jQuery开始的元素句柄。例如elem[‘jQuery203038128.l..537’]这种形式。elemData需要关注另外两个属性:handle和events。
    handler就是一个调用了dispatch的匿名函数,events是一个数组,每一项是一个handleObj对象,包含type、origType、data、handler、guid、selector等属性。如果传递的types为”click focus mouseenter”,那么events数组就包含了三个handleObj对象。
另外还得调用addEventListener给委托元素注册事件,不然事件触发不了。

总得来说,add函数干了几件事:

如果没有为委托元素elem建立缓存,在调用get时创建缓存;
    赋予elemData.handle一个匿名函数,调用event.dispatch函数。
    往elemData.events数组添加不同事件类型的事件对象handleObj。
    给elem绑定一个types类型的事件,触发时调用elemData.handle。

add: function( elem, types, handler, data, selector ) {
var handleObjIn, eventHandle, tmp,
events, t, handleObj,
special, handlers, type, namespaces, origType,
elemData = data_priv.get( elem ); //存储事件句柄对象,elem元素的句柄对象 if ( !handler.guid ) {
handler.guid = jQuery.guid++; //创建编号,为每一个事件句柄给一个标示
} if ( !(events = elemData.events) ) {
events = elemData.events = {}; //events是jQuery内部维护的事件列队
}
if ( !(eventHandle = elemData.handle) ) { //handle是实际绑定到elem中的事件处理函数
eventHandle = elemData.handle = function( e ) {
jQuery.event.dispatch.apply( eventHandle.elem, arguments );
};
eventHandle.elem = elem;
//事件可能是通过空格键分隔的字符串,所以将其变成字符串数组
types = ( types || "" ).match( core_rnotwhite ) || [""];
t = types.length;
while ( t-- ) {
// 这里把handleObj叫做事件处理对象,扩展一些来着handleObjIn的属性
handleObj = jQuery.extend({
type: type,
origType: origType,
data: data,
handler: handler,
guid: handler.guid,
selector: selector,
needsContext: selector && jQuery.expr.match.needsContext.test( selector ),
namespace: namespaces.join(".")
}, handleObjIn ); // 初始化事件处理列队,如果是第一次使用,将执行语句
if ( !(handlers = events[ type ]) ) {
handlers = events[ type ] = [];
handlers.delegateCount = 0; if ( elem.addEventListener ) {
elem.addEventListener( type, eventHandle, false );
}
} // 将事件处理对象推入处理列表,姑且定义为事件处理对象包
if ( selector ) {
handlers.splice( handlers.delegateCount++, 0, handleObj );
} else {
handlers.push( handleObj );
}
// 表示事件曾经使用过,用于事件优化
jQuery.event.global[ type ] = true;
}
// 设置为null避免IE中循环引用导致的内存泄露
elem = null;
}

jQuery.event.dispatch函数

委托元素触发事件时会调用dispatch函数,dispatch函数需要做的就是执行我们添加的handler函数。

jQuery事件中的event和原生event是有区别的,做了扩展。所以代码中重新生成了一个可写的event:jQuery.event.fix(event)。包含的属性:

delegateTarget、currentTarget、handleObj、data、preventDefault、stopPropagation。

由于我们添加的事件函数之前保存到了缓存中,所以调用data_priv.get取出缓存。
    代码生成了一个handlerQueue队列,这里先不忙介绍jQuery.event.handlers函数。handlerQueue是一个数组,每一项是一个格式为{ elem: cur, handlers: matches }的对象。cur是DOM元素,handlers是处理函数数组。

两个while循环:

第一个循环遍历handlerQueue,item为{ elem: cur, handlers: matches }。
    第二个循环遍历handlers,分别执行每一个handler。

event做了封装,我们可以在事件函数中通过event.data获取额外的信息。
    dispatch函数有判断处理函数的返回结果,如果返回结果等于false,阻止冒泡。调用preventDefault、stopPropagation终止后续事件的继续传递。

dispatch: function( event ) {
//把event生成一个可写的对象
event = jQuery.event.fix( event ); var handlers = ( data_priv.get( this, "events" ) || {} )[ event.type ] || [];
event.delegateTarget = this;
handlerQueue = jQuery.event.handlers.call( this, event, handlers ); i = 0;
while ( (matched = handlerQueue[ i++ ]) && !event.isPropagationStopped() ) {
event.currentTarget = matched.elem; j = 0;
while ( (handleObj = matched.handlers[ j++ ]) && !event.isImmediatePropagationStopped() ) {
if ( !event.namespace_re || event.namespace_re.test( handleObj.namespace ) ) {
event.handleObj = handleObj;
event.data = handleObj.data;
ret = handleObj.handler.apply( matched.elem, args );
if ( ret !== undefined ) {
if ( (event.result = ret) === false ) {
event.preventDefault();
event.stopPropagation();
}
}
}
}
} return event.result;
}

jQuery.event.handler函数

dispatch函数有调用handler函数生成一个handler队列,其实整个事件流程中最能体现委托的地方就是handler函数。
    这里有两个端点,cur = event.target(事件触发元素)和this(事件委托元素)。jQuery从cur通过parentNode 一层层往上遍历,通过selector匹配当前元素。
    每一个cur元素都会遍历一次handlers。handlers的项是一个handleObj对象,包含selector属性。通过jQuery( sel, this ).index( cur )判断当前元素是否匹配,匹配成功就加到matches数组。
    handlers遍历完后,如果matches数组有值,就把当前元素cur和matches作为一个对象附加到handlerQueue中。
一个委托元素可能包含委托和普通事件(直接绑定的事件),目前我们只根据delegateCount遍历了委托事件,所以最后还得通过handlers.slice( delegateCount )把后面的普通事件添加到队列中。

什么是委托事件和普通事件?

(“div”).on(“click”,“a,p”,function)这种形式添加的function是div的委托事件;而像(“div”).on(“click”, function)形式添加的事件就是div元素的一个普通事件。handlers数组中delegateCount之前的都是委托事件,之后的是普通事件。

handlers: function( event, handlers ) {
var handlerQueue = [],
delegateCount = handlers.delegateCount,
cur = event.target;
//向上遍历DOM元素
for ( ; cur !== this; cur = cur.parentNode || this ) {
if ( cur.disabled !== true || event.type !== "click" ) {
matches = [];
for ( i = 0; i < delegateCount; i++ ) {
handleObj = handlers[ i ];
//获取handler的selector
sel = handleObj.selector + " "; if ( matches[ sel ] === undefined ) {
matches[ sel ] = handleObj.needsContext ?
//查看通过selector筛选的元素是否包含cur
jQuery( sel, this ).index( cur ) >= 0 :
jQuery.find( sel, this, null, [ cur ] ).length;
}
//如果元素匹配成功,则把handleObj添加到matches数组。
if ( matches[ sel ] ) {
matches.push( handleObj );
}
}
//如果matches数组长度大于0,附加cur和matches到队列中
if ( matches.length ) {
handlerQueue.push({ elem: cur, handlers: matches });
}
}
} if ( delegateCount < handlers.length ) {
//表示还有为委托事件函数,也要附加到队列中
handlerQueue.push({ elem: this, handlers: handlers.slice( delegateCount ) });
} return handlerQueue;
}

如果本篇内容对大家有帮助,请点击页面右下角的关注。如果觉得不好,也欢迎拍砖。你们的评价就是博主的动力!下篇内容,敬请期待!

jQuery源码解读-事件分析的更多相关文章

  1. 【jQuery源码】事件存储结构

    a. jQuery事件原型——Dean Edwards的跨浏览器AddEvent()设计 源码解读   重新梳理一下数据结构,使用一个例子 <input type="text" ...

  2. jQuery源码 Ajax模块分析

    写在前面: 先讲讲ajax中的相关函数,然后结合函数功能来具体分析源代码. 相关函数: >>ajax全局事件处理程序 .ajaxStart(handler) 注册一个ajaxStart事件 ...

  3. jquery源码解读

    最近一直在研读 jQuery 源码,初看源码一头雾水毫无头绪,真正静下心来细看写的真是精妙,其结构明晰,高内聚.低耦合,兼具优秀的性能与便利的扩展性,在浏览器的兼容性(功能缺陷.渐进增强)优雅的处理能 ...

  4. jQuery源码解读三选择器

    直接上jQuery源码截取代码 // Map over jQuery in case of overwrite _jQuery = window.jQuery, // Map over the $ i ...

  5. jQuery源码解读 --- 整体架构

    最近学习比较忙,感觉想要提高还是要读源码,所以准备考试这个考试结束就开始读jquery源码啦,加油~

  6. jQuery源码解读----part 2

    分离构造器 通过new操作符构建一个对象,一般经过四步: A.创建一个新对象 B.将构造函数的作用域赋给新对象(所以this就指向了这个新对象) C.执行构造函数中的代码 D.返回这个新对象 最后一点 ...

  7. jquery源码解读 (摘自jQuery源码分析系列图书(pdf)) 持续更新

    1.总体架构 1.1自调用匿名函数 //自调用匿名函数 (function(window,undefined){ //jquery code})(window); 1.这是一个自调用匿名函数.第一个括 ...

  8. jQuery源码解读 - 数据缓存系统:jQuery.data

    jQuery在1.2后引入jQuery.data(数据缓存系统),主要的作用是让一组自定义的数据可以DOM元素相关联——浅显的说:就是让一个对象和一组数据一对一的关联. 一组和Element相关的数据 ...

  9. 【jQuery源码】事件委托

    jQuery的事件绑定有几个比较优秀的特点: 1. 可以绑定不限数量的处理函数 2. 事件可以委托到祖先节点,不必一定要绑到对应的节点,这样后添加的节点也照样能被处理. 3. 链式操作 下面主要分析事 ...

随机推荐

  1. 用c#开发微信 系列汇总

    网上开发微信开发的教程很多,但c#相对较少.这里列出了我所有c#开发微信的文章,方便自己随时查阅.   一.基础知识 用c#开发微信(1)服务号的服务器配置和企业号的回调模式 - url接入 (源码下 ...

  2. SqlServer2012 数据库的同步之SQL JOB + 建立链接服务器

         文章参考百度过的文章,现在忘了具体哪篇,感谢其分享,这里根据自己的操作和遇到的问题整理一下.      需求:在两个不同的SQL SERVER 2012的服务器之间进行数据访问和更新.我们需 ...

  3. 细数.NET 中那些ORM框架 —— 谈谈这些天的收获之一

    细数.NET 中那些ORM框架 —— 谈谈这些天的收获之一(转) ADO.NET Entity Framework        ADO.NET Entity Framework 是微软以 ADO.N ...

  4. Windows桌面共享中一些常见的抓屏技术

    1. BitBlt 我想做Windows开发应该都知道这个API, 它能实现DC间的内容拷贝, 如果我们把源DC指定成Monitor DC或是桌面DC, 它就能实现抓屏功能. 对于通过这种方式的抓屏, ...

  5. 三天学会HTML5 之第一天

    引言 HTML5 一直是非常热门的话题,因此此系列文章主要从一些基本功能开始讲起,逐步深入了解HTML5的新概念. 首先了解一些基本的术语和概念. SGML, HTML,XML三者之间的区别 Doc类 ...

  6. JavaScript 常用功能总结

    小编吐血整理加上翻译,太辛苦了~求赞! 本文主要总结了JavaScript 常用功能总结,如一些常用的JS 对象,基本数据结构,功能函数等,还有一些常用的设计模式. 目录: 众所周知,JavaScri ...

  7. React Native01-开始 Windows环境安装配置篇

    转载本文章的童鞋请注明原链接. 查阅文档之类的资料,建议到 http://reactnative.cn/ 本人使用环境Win10. 在阅读本文之前,请了解我们安装React Native之前,要安装P ...

  8. IOS 手势-轻点、触摸、手势、事件

    1.概念 手势是从你用一个或多个手指接触屏幕时开始,直到手指离开屏幕为止所发生的所有事件.无论手势持续多长时间,只要一个或多个手指仍在屏幕上,这个手势就存在. 触摸是指把手指放到IOS设备的屏幕上,从 ...

  9. vue在多级联动时,一些情况不用watch而用onchange会更好

    onchange事件在内容改变且失去焦点时触发,因此在一些多级联动需要清空次级内容的时候,用onchange就非常有用了,尤其是浏览器会提前加载数据的情况下.有篇文章可以看一下,链接. PS:路漫漫其 ...

  10. Redis学习笔记~常用命令总结

    回到目录 客户端redis-cli常用的命令总结 连接到服务器 redis-cli -h 127.0.0.1 -p 6379 --连接指定的redis服务器 发布/订阅, pub/sub模式运行在re ...