JS事件绑定、js事件流、js事件循环机制

1、JS事件绑定

如何像DOM元素添加事件?通常有三种方法

  1. 在dom元素中绑定
  2. 在js中绑定
  3. 绑定监听事件函数

1、onclick、onmouseover、onmouseout、onmousedown、onmouseup、ondblclick、onkeydown、onkeypress、onkeyup

<input type="button" value="click me" onclick="hello()">
 
<script>
    function hello(){
     	alert("hello world!");
    }
</script>

2、

<input type="button" value="click me" id="btn">
 
<script>
    document.getElementById("btn").onclick = function(){
     	alert("hello world!");
    }
</script>

3、用 addEventListener() 或 attachEvent() 来绑定事件监听函数。

element.addEventListener(eventfunctionuseCapture)

event:指定事件名

function:事件触发执行函数

useCapture:指定事件流的的方向

事件监听的优点:

  1. 常规的事件绑定只执行最后绑定的事件。事件监听可以绑定多个事件。
  2. 可以解除事件绑定
2、JS事件流

事件流分为事件冒泡和事件捕获

当点击事件发生的时候,我们并不认为只有当前节点被点击,而是他的父节点,整个页面也被点击了。

ie和Netscape开发团队提出了两种事件流的概念。

ie的叫做事件冒泡,事件从最具体的元素接收,然后逐级向上传播。

netScape的叫做事件捕获,事件从最不具体的元素先接收,然后逐级向下传播,直到最具体的元素接收。

  • egHTMl:1.事件冒泡-事件捕获.html

指定事件传递方式

addEventListener中的第三个参数是useCapture, 一个bool类型。
设置为true就在事件捕获过程中执行(由外向里),反之就在事件冒泡过程中执行处理函数(由里向外)。
div1 包含 div2

document.getElementById('id1').addEventListener('click', function() { console.log('id1');}, false); 

div2 -》 div1
 
document.getElementById('id2').addEventListener('click', function() { console.log('id2');}, true);
div1 -> div2
3、事件委托(也叫事件代理)

把一个元素的事件委托到另一个元素上

原理:利用事件冒泡原理实现,事件从最深的节点开始,逐步向上传播事件,指定较外层节点处理事件。

好处:

  1. 节省内存空间,不需要绑定很多对象,只绑定一个父对象就可以。
  2. 提高Javascript性能
  3. 可以为新添加的Dom元素动态添加事件

手写一个事件委托

???????????

#####

4、阻止事件冒泡和默认事件

阻止冒泡

event.stopPropagation();

阻止默认事件

event.preventDefault();
5、js事件循环机制

js是单线程非阻塞的脚本语言。那么单线程语言如何实现事件循环机制的呢??

单线程:

浏览器中,我们需要进行各种各样的dom操作。试想一下 如果javascript是多线程的,那么当两个线程同时对dom进行一项操作,例如一个向其添加事件,而另一个删除了这个dom,此时该如何处理呢?因此,为了保证不会 发生类似于这个例子中的情景,javascript选择只用一个主线程来执行代码,这样就保证了程序执行的一致性

非阻塞:

按道理js单线程应该是阻塞的,javascript引擎到底是如何实现的非阻塞这一点呢?

js本身不可能异步,但是js的宿主环境是多线程的,宿主通过event loop(事件循环)让js拥有了异步的能力。

事件循环

由于大多数现代系统内核是多线程的、同理浏览器内核也是多线程的,内核可以处理后台执行的多个操作。当其中一个操作完成的时候,内核告诉 Node.js,相应的回调就被添加到轮询队列(poll queue)并最终得到执行。

所有的事件被分为两种,同步任务-执行栈中、异步任务-事件队列中、

执行栈与事件队列

  • 执行栈:

我们知道,当我们调用一个方法的时候,js会生成一个与这个方法对应的执行环境(context),又叫执行上下文。这个执行环境中存在着这个方法的私有作用域,上层作用域的指向,方法的参数,这个作用域中定义的变量以及这个作用域的this对象。 而当一系列方法被依次调用的时候,因为js是单线程的,同一时间只能执行一个方法,于是这些方法被排队在一个单独的地方。这个地方被称为执行栈。

当一个脚本第一次执行的时候,js引擎会解析这段代码,并将其中的同步代码按照执行顺序加入执行栈中,然后从头开始执行。如果当前执行的是一个方法,那么js会向执行栈中添加这个方法的执行环境,然后进入这个执行环境继续执行其中的代码。当这个执行环境中的代码 执行完毕并返回结果后,js会退出这个执行环境并把这个执行环境销毁,回到上一个方法的执行环境。。这个过程反复进行,直到执行栈中的代码全部执行完毕。

  • 事件队列:(事件队列中的函数执行是浏览器开辟的新线程执行的)

js引擎遇到一个异步事件后并不会一直等待其返回结果,而是会将这个事件挂起,继续执行执行栈中的其他任务。当一个异步事件返回结果后,js会将这个事件加入与当前执行栈不同的另一个队列,我们称之为事件队列。被放入事件队列不会立刻执行其回调,而是等待当前执行栈中的所有任务都执行完毕, 主线程处于闲置状态时,主线程会去查找事件队列是否有任务。如果有,那么主线程会从中取出排在第一位的事件,并把这个事件对应的回调放入执行栈中,然后执行其中的同步代码…,如此反复,这样就形成了一个无限的循环。这就是这个过程被称为“事件循环(Event Loop)”的原因。

  • macro task与micro task

异步任务的优先级不同,因此也有分为宏任务和微任务

以下事件属于宏任务:

  • setInterval()
  • setTimeout()

以下事件属于微任务

  • new Promise()
  • new MutaionObserver()

在当前执行栈为空的时候,主线程会查看微任务队列是否有事件存在。如果不存在,那么再去宏任务队列中取出一个事件并把对应的回到加入当前执行栈;如果存在,则会依次执行队列中事件对应的回调,直到微任务队列为空,然后去宏任务队列中取出最前面的一个事件,把对应的回调加入当前执行栈…如此反复,进入循环。

setTimeout(function () {
    console.log(1);
});

new Promise(function(resolve,reject){
    console.log(2)
    resolve(3)
}).then(function(val){
    console.log(val);
})
结果是什么呢?
231

我们上面讲的是js的event loop,nodeJs的事件循环则稍有不同,会有一些不确定性。

js中异步和同步有哪些方法

同步:console.log()、alert()、

异步:promise

setTimeOut与promise异步区别

setTimeOut()、Promise()、