1 事件循环机制
1.1多线程的浏览器和单线程的JavaScript
众所周知,JS只有一个主线程,那么在执行所有任务的时候只能一个一个来吗?就像是排队看病一样,医生一次只能接待一个病人。那么如果这个病人需要去化验,等待结果的这段时间医生还是等待这个病人化验吗?还是会先接待后面的病人?JS有自己的机制来解决这个问题。
通俗来讲,操作分为:发出调用和得到结果。发出调用立刻得到结果是为同步,发出调用无法立刻得到结果,需要额外的操作才能得到结果是为异步。同步是调用之后一直等待,直到返回结果,异步则是调用之后需要通过一系列的手段才能拿到结果(发出调用,在拿到结果的这段事件可以介入其他任务)。
上面所说的一系列手段就是实现异步的方法,其中就包括:event loop。以及轮询、事件等。
轮询:就如同在点菜之后,不断去问服务员才有没有好。
事件:就如同点菜之后,不用不断的问,饭菜好了之后服务员自己会通知菜好了。
虽然JS是单线程的,但是t他的宿主浏览器却不是,一个浏览器中有多个常驻线程。
1.Js引擎线程:JavaScript引擎是基于事件驱动单线程执行的,JavaScript引擎一直等待着任务队列中任务的到来,然后加以处理,浏览器无论什么时候都只有一个JavaScript线程在运行JavaScript程序。
2.Gui渲染线程: GUI渲染线程负责渲染浏览器界面,当界面需要重绘(Repaint)或由于某种操作引发回流(Reflow)时,该线程就会执行。但需要注意,GUI渲染线程与JavaScript引擎是互斥的,当JavaScript引擎执行时GUI线程会被挂起,GUI更新会被保存在一个队列中等到JavaScript引擎空闲时立即被执行。
3.事件触发线程: 当一个事件被触发时该线程会把事件添加到待处理队列的队尾,等待JavaScript引擎的处理。这些事件可来自JavaScript引擎当前执行的代码块如setTimeout、也可来自浏览器内核的其他线程如鼠标点击、Ajax异步请求等,但由于JavaScript的单线程关系所有这些事件都得排队等待JavaScript引擎处理(当线程中没有执行任何同步代码的前提下才会执行异步代码)。
还会开辟一些其他线程,例如http请求线程等等(用完就关闭)
1.2 浏览器的事件循环(event loop)
1.函数入栈,当Stack中执行到异步任务的时候,就将他丢给WebAPIs,然后继续执行同步任务,直到Stack为空;
2.在此期间WebAPIs完成这个异步任务并产生一个事件,把这个事件(回调函数)放入CallbackQueue中等待(js主线程执行异步任务,就是在执行回调函数);
3.当执行栈为空时,Event Loop把Callback Queue中的一个任务放入Stack中,回到第1步。
#stack 执行栈,JS主线程来处理
#Event Loop事件循环是由javascript宿主环境(像浏览器)来轮询实现的;使得主线程从执行队列中不断获取任务(异步事件的回调)。
#WebAPIs是由C++实现的浏览器创建的线程,处理诸如DOM事件、http请求、定时器等异步事件;
#JavaScript 的并发模型基于"事件循环";
#Callback Queue(Event Queue 或者 Message Queue) 任务队列,存放异步事件的回调函数
2 事件机制
2.1 Dom节点的删除与事件绑定
在删除了Dom节点的时候,是否需要手动删除绑定在其上的事件。在删除一个节点的时候,浏览器会将删除掉的节点的兄弟指针和父指针置为null,并没有delete。所以此时,这个节点依然存在于内存中,事件依然存在。什么时候,这个节点会被析构呢?当发生GC回收的时候,会将不存在引用的Dom节点的内存释放掉,并删除map里面的数据。所以并不需要手动删除事件。
2.2 Dom的Level 0 事件
指的是dom节点的onclick onfocus等属性。
onClick 和 addEventLisener: onClick的绑定实际上仍然调用的是addEventLisener,只是在此之前会先clear掉上一次绑定的属性事件。所以onClick和addEvnetLisener并不冲突。
1.onclick事件在同一时间只能指向唯一对象
2.addEventListener给一个事件注册多个listener
3.addEventListener对任何DOM都是有效的,而onclick仅限于HTML
4.addEventListener可以控制listener的触发阶段,(捕获/冒泡)。对于多个相同的事件处理器,不会重复触发,不需要手动使用removeEventListener清除
5.IE9使用attachEvent和detachEvent
Dom.onClick(()=>console.log(‘第一次’)
Dom.onClick(()=>console.log(“第二次”)
只会触发一次,输出‘第二次’
Dom.addEventLisener(‘click’,()=>console.log(‘第一次’))
Dom.addEventLisener(‘click’,()=>console.log(‘第二次’))
会触发两次,依次触发。
2.3 事件的捕获目标冒泡
1.捕获(Capture):事件对象(event object) 从 window 派发到 目标对象父级的过程。
2.目标(Target): 事件对象派发到目