以下题目是根据网上多份面经收集而来的,题目相同意味着被问的频率比较高,有问题欢迎留言讨论,喜欢可以点赞关注。
https://www.jianshu.com/p/5f1a8f586019
1、介绍下事件代理,主要解决什么问题,有什么优缺点
①事件代理也叫事件委托,原理:事件冒泡机制
事件委托是利用事件的冒泡原理来实现的,就是事件从最深的节点开始,然后逐步向上传播事件。页面上有这么一个节点树,div>ul>li>a;比如给最里面的a加一个click点击事件,那么这个事件就会一层一层的往外执行,执行顺序a>li>ul>div,有这样一个机制,那么我们给最外面的div加点击事件,那么里面的ul,li,a做点击事件的时候,都会冒泡到最外层的div上,所以都会触发,这就是事件委托,委托它们父级代为执行事件。
②解决什么问题:
减少事件注册,在li上注册事件要一百个,用遍历的方法消耗性能,代理代码简单高效
③优缺点
优点:
1.节省内存占用,减少事件注册。比如ul上代理所有li的click事件就很不错。
2.可以实现当新增子对象时,无需再对其进行事件绑定,删除子对象也不需解绑
缺点:
1、事件代理的常用应用应该仅限于上述需求,如果把所有事件都用事件代理,可能会出现事件误判。即本不该被触发的事件被绑定上了事件。比如点击列表中的按钮确进入了详情页面
2、不适应所有的事件,只适用于支持事件冒泡的事件
2、事件循环机制(event loop),node和浏览器的事件循环机制区别
浏览器中的 Event Loop
浏览器端事件循环中的异步队列有两种:macro(宏任务)队列和 micro(微任务)队列。宏任务队列可以有多个,微任务队列只有一个。
- 常见的 macro-task 比如:setTimeout、setInterval、script(整体代码)、 I/O 操作、UI 渲染等。
- 常见的 micro-task 比如: new Promise().then(回调)、MutationObserver(html5新特性) 等。
一开始执行栈空,我们可以把执行栈认为是一个存储函数调用的栈结构,遵循先进后出的原则。micro 队列空,macro 队列里有且只有一个 script 脚本(整体代码)。
全局上下文(script 标签)被推入执行栈,同步代码执行。在执行的过程中,会判断是同步任务还是异步任务,通过对一些接口的调用,可以产生新的 macro-task 与 micro-task,它们会分别被推入各自的任务队列里。同步代码执行完了,script 脚本会被移出 macro 队列,这个过程本质上是队列的 macro-task 的执行和出队的过程。
上一步我们出队的是一个 macro-task,这一步我们处理的是 micro-task。但需要注意的是:当 macro-task 出队时,任务是一个一个执行的;而 micro-task 出队时,任务是一队一队执行的。因此,我们处理 micro 队列这一步,会逐个执行队列中的任务并把它出队,直到队列被清空。
执行渲染操作,更新界面
检查是否存在 Web worker 任务,如果有,则对其进行处理
上述过程循环往复,直到两个队列都清空
区别
浏览器和Node 环境下,microtask 任务队列的执行时机不同
- Node端,microtask 在事件循环的各个阶段之间执行
- 浏览器端,microtask 在事件循环的 macrotask 执行完之后执行
3、如何让事件先冒泡后捕获
在DOM标准事件模型中,是先捕获后冒泡。但是如果要实现先冒泡后捕获的效果,对于同一事件,监听捕获和冒泡,分别对应响应的处理函数,监听到捕获事件,先暂缓执行,直到冒泡事件被捕获后再执行捕获事件。
let w = document.getElementsByClassName('wraper')[0]
let b = document.getElementsByClassName('buller')[0]
let i = document.getElementsByClassName('inner')[0]
// 冒泡阶段
w.addEventListener('click', function () {
console.log('capture wraper');
}, false)
b.addEventListener('click', function () {
console.log('capture buller');
}, false)
i.addEventListener('click', function () {
console.log('capture inner');
}, false)
// 捕获阶段
w.addEventListener('click', function () {
setTimeout(function () {
console.log('wraper');
}, 0)
}, true)
b.addEventListener('click', function () {
setTimeout(function () {
console.log('buller');
}, 0)
}, true)
i.addEventListener('click', function () {
setTimeout(function () {
console.log('inner');
}, 0)
}, true)
4、说一下浏览器事件,各种父类子类设置冒泡或者捕获,哪个先哪个后
5、使用addEventListener点击li弹出内容,并且动态添加li之后有效
6、刚才你提到了事件流(event loop),能简单的说一下,什么是事件流吗
我们都知道,js是单线程的,虽然现在有 worker
的存在,但是也只是可以进行运算,并不能操作 dom
;js最一开始执行的线程,是主线程,然后主线程执行完毕后,是微队列 microtask
的循环执行,微队列执行完毕后,在执行宏队列 macrotask
;
宏队列的方法:setTimeout
、setInterval
、setImmediate
、I/O
、UI rendering
微队列的方法:promise.then
、process.nextTick
、Object.observe(已废弃)
正好之前写过这方面的文章,想详细的理解这一块的知识,跳转:性能优化篇 - js事件循环机制(event loop)
其实,event loop它最主要是分三部分:主线程、宏队列(macrotask)、微队列(microtask)
js的任务队列分为同步任务和异步任务,所有的同步任务都是在主线程里执行的,异步任务可能会在macrotask或者microtask里面
其实,event loop用简单点的话去解释,就是:
1、先执行主线程
2、遇到宏队列(macrotask)放到宏队列(macrotask)
3、遇到微队列(microtask)放到微队列(microtask)
4、主线程执行完毕
5、执行微队列(microtask),微队列(microtask)执行完毕
6、执行一次宏队列(macrotask)中的一个任务,执行完毕
7、执行微队列(microtask),执行完毕
8、依次循环。。。
7、JS运行机制
https://www.jianshu.com/p/e06e86ef2595
8、线程和进程和区别
javascript是一门单线程语言,在最新的HTML5中提出了Web-Worker,但javascript是单线程这一核心仍未改变。所以一切javascript版的"多线程"都是用单线程模拟出来的,一切javascript多线程都是纸老虎!
进程:是并发执行的程序在执行过程中分配和管理资源的基本单位,是一个动态概念,竞争计算机系统资源的基本单位。
线程:是进程的一个执行单元,是进程内科调度实体。比进程更小的独立运行的基本单位。线程也被称为轻量级进程。
一个程序至少一个进程,一个进程至少一个线程。
进程与线程的区别:
1、进程是资源分配的最小单位,线程是程序执行的最小单位(资源调度的最小单位)
2、进程有自己的独立地址空间,每启动一个进程,系统就会为它分配地址空间,建立数据表来维护代码段、堆栈段和数据段,这种操作非常昂贵。而线程是共享进程中的数据的,使用相同的地址空间,因此CPU切换一个线程的花费远比进程要小很多,同时创建一个线程的开销也比进程要小很多。
3、线程之间的通信更方便,同一进程下的线程共享全局变量、静态变量等数据,而进程之间的通信需要以通信的方式(IPC)进行。不过如何处理好同步与互斥是编写多线程程序的难点。
4、但是多进程程序更健壮,多线程程序只要有一个线程死掉,整个进程也死掉了,而一个进程死掉并不会对另外一个进程造成影响,因为进程有自己独立的地址空间。
参考:https://www.cnblogs.com/zhehan54/p/6130030.html
9、说说浏览器的事件代理机制的原理
同一
10、简述下浏览器的Event loop
11、浏览器事件流向
DOM事件传播包括三个阶段:
1、捕获阶段
2、目标对象调用事件处理程序
3、冒泡阶段
希望注册在DOM元素上的事件处理程序在捕获阶段还是在冒泡阶段触发,取决于 addEventListener() 方法的第三个参数为 true 还是 false
12、事件冒泡是什么,写一个阻止事件冒泡的js
1.event.stopPropagation(); // 阻止了事件冒泡,但不会阻击默认行为
2.return false; //阻止全部事件
3.event.preventDefault(); //阻止默认事件
13、宏任务(macro-task)微任务(micro-tack)
宏队列的方法:setTimeout
、setInterval
、setImmediate
、I/O
、UI rendering
微队列的方法:promise.then
、process.nextTick
、Object.observe(已废弃)
14、setTimeout和setInterva
w3c在HTML标准中规定,要求setTimeout时间低于4ms的都按4ms来算。这里还要注意一点,就是有些时候,为什么一些大神用js做一些类似于动画操作的时候,喜欢用setTimeout,而不是用setInterval呢,因为setTimeout是在这个时间后,把当前的方法推到任务队列里,而setInterval是强行把当前的方法添加到任务队列里,这样可能会对当前页面的用户体验很不好,可能会出现效果卡顿的情况,所以这里还要注意一点:setTimeout是延迟执行,但是不是延迟的时间后立即执行的。
15、我们来分析一段较复杂的代码,看看你是否真的掌握了js的执行机制
console.log('1');
setTimeout(function() {
console.log('2');
process.nextTick(function() {
console.log('3');
})
new Promise(function(resolve) {
console.log('4');
resolve();
}).then(function() {
console.log('5')
})
})
process.nextTick(function() {
console.log('6');
})
new Promise(function(resolve) {
console.log('7');
resolve();
}).then(function() {
console.log('8')
})
setTimeout(function() {
console.log('9');
process.nextTick(function() {
console.log('10');
})
new Promise(function(resolve) {
console.log('11');
resolve();
}).then(function() {
console.log('12')
})
})
16、target和currentTarget的区别
1、target:触发事件的元素。currentTarget:事件绑定的元素。
2、两者在没有冒泡的情况下,是一样的值,但在用了事件委托的情况下,就不一样了;
例如:
<ul id="ulT">
<li class="item1">fsda</li>
<li class="item2">ewre</li>
<li class="item3">qewe</li>
<li class="item4">xvc</li>
<li class="item5">134</li>
</ul>
<script type="text/javascript">
document.getElementById("ulT").addEventListener('click', function () {
console.log(event.target);//item
console.log(event.currentTarget);//ulT
})
</script>