又是一个重磅功能点。
在分析源码之前分析一下体系结构,有助于源码理解。实际上在jQuery出现之前,Dean Edwards的跨浏览器AddEvent()设计做的已经比较优秀了;而且jQuery事件系统的设计思想也是基于该思想的,所以我们先分析一下Dean Edwards前辈的事件绑定。
a. jQuery事件原型——Dean Edwards的跨浏览器AddEvent()设计
源码解读
//事件添加方法 function addEvent(element, type, handler) { //保证每个不同的事件响应函数只有唯一一个id if (!handler.$$guid) handler.$$guid = addEvent.guid++; // 给element维护一个events属性,初始化为一个空对象。 // element.events的结构类似于 { "click": {...}, "dbclick": {...}, "change": {...} } if (!element.events) element.events = {}; // 试图取出element.events中当前事件类型type对应的对象(这个对象更像数组),赋值给handlers //如果element.events中没有当前事件类型type对应的对象则初始化 var handlers = element.events[type]; if (!handlers) { handlers = element.events[type] = {}; // 如果这个element已经有了一个对应的事件的响应方法,例如已经有了onclick方法 // 就把element的onclick方法赋值给handlers的0元素,此时handlers的结构就是: // { 0: function(e){...} },这也是为什么addEvent.guid初始化为1的原因,预留看为0的空间; // 此时element.events的结构就是: { "click": { 0: function(e){...} }, /*省略其他事件类型*/ } if (element["on" + type]) { handlers[0] = element["on" + type]; } } // 把当前的事件handler存放到handlers中,handler.$$guid = addEvent.guid++; addEvent.guid = 1; 肯定是从1开始累加的 //因此,这是handlers的结构可能就是 { 0: function(e){...}, 1: function(){}, 2: function(){} 等等... } handlers[handler.$$guid] = handler; //下文定义了一个handleEvent(event)函数,将这个函数,绑定到element的type事件上作为事件入口。 //说明:在element进行click时,将会触发handleEvent函数,handleEvent函数将会查找element.events,并调用相应的函数。可以把handleEvent称为“主监听函数” element["on" + type] = handleEvent; }; //计数器 addEvent.guid = 1; function removeEvent(element, type, handler) { // delete the event handler from the hash table if (element.events && element.events[type]) { delete element.events[type][handler.$$guid]; } }; function handleEvent(event) { //兼容ie event = event || window.event; //this是响应事件的节点,这个接点上有events属性(在addEvent中添加的) //获取节点对应事件响应函数列表 var handlers = this.events[event.type]; // 循环响应函数列表执行 for (var i in handlers) { //保持正确的作用域,即this关键字 this.$$handleEvent = handlers[i]; this.$$handleEvent(event); } };
重新梳理一下数据结构,使用一个例子
<input type="text" id="chua" onClick="f0();"> function f0(){...} function f1(){...} function f2(){...} function f3(){...} var dom = document.getElementById("chua"); addEvent(dom,"click",f1); addEvent(dom,"change",f1); addEvent(dom,"change",f2); addEvent(dom,"click",f3); addEvent(dom,"change",f3);
经过addEvent()函数之后,当前的数据结构为:
element: { onclick: handleEvent(event), //click事件的主监听函数 onchage: handleEvent(event), //change事件的主监听函数 events: { click:{//这是一个类数组 0: f0, //element已有的事件 1: f1, //下标1实际上就是f1.$$guid 3: f3 //下标3实际上就是f3.$$guid,需要注意的是每一个响应事件都有一个唯一的$$guid作为下标 ... }, change:{//这是一个类数组 1: f1, 2: f2, 3: f3 } } }
事件系统会根据调用addEvent的顺序给每个响应函数(也就是addEvent(element, type, handler)中的第三个参数handler)打上标记$$guid。源码
//保证每个不同的事件响应函数只有唯一一个id if (!handler.$$guid) handler.$$guid = addEvent.guid++;
最终三个响应函数的$$guid标记分别是
f1.$$guid = 1
f2.$$guid = 2
f3.$$guid = 3
而根据源码中
handlers[handler.$$guid] = handler;
那么某一个函数在任何事件响应函数集合中的下标位置是固定的。比如click和change事件都调用f3作为响应事件,那么f3在element.events.click以及element.events.change中的下标位置都是f3.$$guid = 3;即element.events.click[3] = element.events.change[3] = f3。
这个时候假设又新添了一个事件绑定:addEvent(dom,"focus",f3);那么element.events.focus[3] = f3;这也是对象相比于数组的方便之处,数组不可能没有下标0,1,2就直接有3了,但是对象却可以,此时3是作为对象的一个属性名称。
这样的设计,其实已经具备了jquery事件系统的雏形,包含了几个最主要的特点:
1)element上的所有事件,将保存到element.events属性中,不是直接绑定到element上;这样一个事件可以有无数个响应函数。
2)handleEvent作为element所有事件的“主监听函数”,有它统一管理element上的所有函数。
3)所有浏览器都支持element["on" + type]事件绑定方式,跨浏览器兼容。
好啦,明白了addEvent的事件结构,这个想法确实让人觉得眼前一亮。下面分析jQuery的事件结构
b. jQuery的事件结构
所有的函数添加事件都会进入jQuery.event.add函数。该函数有两个主要功能:添加事件、附加很多事件相关信息。我们直接上源码,源码思想和Dean Edwards的跨浏览器兼容事件添加处理类似。
源码分析
add: function( elem, types, handler, data, selector ) { var tmp, events, t, handleObjIn, special, eventHandle, handleObj, handlers, type, namespaces, origType, //获取elem节点对应的缓存数据 elemData = jQuery._data( elem ); //没有数据或文本/注释节点不能附加事件(但是允许附加普通对象) if ( !elemData ) { return; } //调用者能通过自定义数据替换handler if ( handler.handler ) { handleObjIn = handler; handler = handleObjIn.handler; selector = handleObjIn.selector; } //确保handler函数有唯一的ID,后续会用来查找/删除这个handler函数 if ( !handler.guid ) { handler.guid = jQuery.guid++; } //如果是初次进入,初始化元素的事件结构和主事件响应入口 if ( !(events = elemData.events) ) { events = elemData.events = {}; } if ( !(eventHandle = elemData.handle) ) { eventHandle = elemData.handle = function( e ) { //当一个事件被调用后页面已经卸载,则放弃jQuery.event.trigger()的第二个事件, return typeof jQuery !== core_strundefined && (!e || jQuery.event.triggered !== e.type) ? jQuery.event.dispatch.apply( eventHandle.elem, arguments ) : undefined; }; //将elem作为handle函数的一个特征防止ie非本地事件引起的内存泄露 eventHandle.elem = elem; } //多个事件使用空格隔开的处理 //如jQuery(...).bind("mouseover mouseout", fn); //core_rnotwhite = /\S+/g;匹配空白字符 types = ( types || "" ).match( core_rnotwhite ) || [""]; t = types.length; while ( t-- ) { //rtypenamespace = /^([^.]*)(?:\.(.+)|)$/; //获取命名空间和原型事件 tmp = rtypenamespace.exec( types[t] ) || []; type = origType = tmp[1]; namespaces = ( tmp[2] || "" ).split( "." ).sort(); //如果事件改变其类型,使用special事件处理器来处理更改后的事件类型 special = jQuery.event.special[ type ] || {}; //如果选择器已定义,确定special事件API类型,否则给他一个类型 type = ( selector ? special.delegateType : special.bindType ) || type; //基于新设置的类型更新special special = jQuery.event.special[ type ] || {}; // handleObj贯穿整个事件处理 handleObj = jQuery.extend({ type: type, origType: origType, data: data, handler: handler, guid: handler.guid, selector: selector, // For use in libraries implementing .is(). We use this for POS matching in `select` //"needsContext": new RegExp( "^" + whitespace + "*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\(" + //whitespace + "*((?:-\\d)?\\d*)" + whitespace + "*\\)|)(?=[^-]|$)", "i" ) //用来判断亲密关系 needsContext: selector && jQuery.expr.match.needsContext.test( selector ), namespace: namespaces.join(".") }, handleObjIn ); //初次使用时初始化事件处理器队列 if ( !(handlers = events[ type ]) ) { handlers = events[ type ] = []; handlers.delegateCount = 0; //非自定义事件,如果special事件处理器返回false,则只能使用addEventListener/attachEvent if ( !special.setup || special.setup.call( elem, data, namespaces, eventHandle ) === false ) { //给元素绑定全局事件 if ( elem.addEventListener ) { elem.addEventListener( type, eventHandle, false ); } else if ( elem.attachEvent ) { elem.attachEvent( "on" + type, eventHandle ); } } } //自定义事件绑定 if ( special.add ) { special.add.call( elem, handleObj ); if ( !handleObj.handler.guid ) { handleObj.handler.guid = handler.guid; } } //将事件对象handleObj添加到元素的处理列表,代理计数递增 if ( selector ) { handlers.splice( handlers.delegateCount++, 0, handleObj ); } else { handlers.push( handleObj ); } //跟踪那个事件曾经被使用过,用于事件优化 jQuery.event.global[ type ] = true; } //防止ie内存泄漏 elem = null; }
依然用实例来说明jQuery的事件结构
<div id="#center"></div> <script> function dohander(){console.log("dohander")}; function dot(){console.log("dot");} $(document).on("click",'#center',dohander) .on("click",'#center',dot) .on("click",dot); </script>
经过添加处理环节,事件添加到了元素上,而且节点对应的缓存数据也添加了相应的数据。结构如下
elemData = jQuery._data( elem ); elemData = { events: { click: {//Array[3] 0: { data: undefined/{...}, guid: 2, //处理函数的id handler: function dohander(){…}, namespace: "", needsContext: false, origType: "click", selector: "#center",//选择器,用来区分不同事件源 type: "click" } 1: { data: undefined/{...}, guid: 3, handler: function dot(){…}, namespace: "", needsContext: false, origType: "click", selector: "#center", type: "click" } 2: { data: undefined, guid: 3, handler: function dot(){…}, namespace: "", needsContext: false, origType: "click", selector: undefined, type: "click" } delegateCount: 2,//委托事件数量,有selector的才是委托事件 length: 3 } } handle: function ( e ) {…}/*事件处理主入口*/{ elem: document//属于handle对象的特征 } }
jQuery的处理和Dean Edwards的跨浏览器兼容事件添加处理类似,比如为每一个函数添加guid;使用events对象存放响应事件列表,有一个总的事件处理入口handle等。
jQuery做了哪些改进?
1)事件数据不再直接保存在节点上,而是使用jQuery缓存系统内(内部使用的缓存jQuery._data方式存取)
2)事件委托:绑定到当前节点(例子中当前节点是document根节点)的处理函数不仅仅包含当前节点触发事件(click)响应时处理的事件(例子中selector为undefined时对应的处理函数dot);还代理了其他节点(例子中的#center节点)触发事件(click)响应时处理的事件(例子中selector为"#center"对应的处理事件doHandler和dot);委托机制在后续分析。
3)增加了很多功能数据,比如命名空间namespace:这个主要用在自定义事件自定义触发,比如$(document).on("chua.click",'#center',dot),主动触发$("#center").trigger("chua.click")。还有额外数据data:虽然没有看到那个地方有被用到。
到此jQuery的事件结构就清楚了。后面再分析事件的绑定和触发以及委托原理。

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 사용 및 과도한 폐쇄 사용을 피하는 것이 포함됩니다.

Python은 부드러운 학습 곡선과 간결한 구문으로 초보자에게 더 적합합니다. JavaScript는 가파른 학습 곡선과 유연한 구문으로 프론트 엔드 개발에 적합합니다. 1. Python Syntax는 직관적이며 데이터 과학 및 백엔드 개발에 적합합니다. 2. JavaScript는 유연하며 프론트 엔드 및 서버 측 프로그래밍에서 널리 사용됩니다.


핫 AI 도구

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

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

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

Clothoff.io
AI 옷 제거제

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

인기 기사

뜨거운 도구

Eclipse용 SAP NetWeaver 서버 어댑터
Eclipse를 SAP NetWeaver 애플리케이션 서버와 통합합니다.

맨티스BT
Mantis는 제품 결함 추적을 돕기 위해 설계된 배포하기 쉬운 웹 기반 결함 추적 도구입니다. PHP, MySQL 및 웹 서버가 필요합니다. 데모 및 호스팅 서비스를 확인해 보세요.

Atom Editor Mac 버전 다운로드
가장 인기 있는 오픈 소스 편집기

Dreamweaver Mac版
시각적 웹 개발 도구

SublimeText3 Linux 새 버전
SublimeText3 Linux 최신 버전
