大三学生总结JS进阶面试题

一、event loop

1、什么是event loop

        event loop(事件循环/事件轮询)

        由于js是单线程运行的,异步要基于回调来实现,event loop就是异步回调的实现原理

2、event loop执行过程

Call Stack:调用栈        web APIs:浏览器定义的API(执行异步的API)

Event Loop:事件循环        Callback Queue:回调队列

        同步代码会放在Call Stack中,一行行执行

        遇到异步,放在web APIs中“记录”下来,等待时机(定时,网络请求等)

        时机一到,web APIs就会把异步代码放进Callback Queue里

        当同步代码执行完(call Stack为空),会先尝试DOM渲染,修改DOM结构(比如给div增加p标签),渲染完后,Event Loop就会开始工作

        循环查找Callback Queue,如果有异步代码,则会移动到Call Stack内执行,再次DOM渲染。直至Callback Queue内的异步代码全部移除

3、DOM事件和Event loop的关系

        DOM事件也使用回调,基于Event Loop

        如上图所示,当Hi打印后会执行btn1的click事件,将click事件内的回调函数放入web APIs内。同步代码执行完(call Stack为空)后,Event Loop就会开始工作。如果用户点击btn1,回调函数会被移动Callback Queue里,Enent Loop循环查找Callback Queue,将它放入Call Stack中执行

二、aysnc-await

1、async和await语法介绍

        异步回调有callback hell风险

        promise then catch链式调用,但也是基于回调函数

        async/await能用同步的语法来写异步代码,彻底消灭的回调函数

2、async-await和promise的关系

        async-await是消灭异步回调的武器,但它并不和promise互斥,反而两者相辅相成

        执行async函数,返回的是promise对象

        await相当于promise的then

        try...catch可以捕获异常,代替了promise的catch

3、async-await进阶

        async-await是语法糖,异步的本质还是回调函数

        await后面,可以都看作回调内容,是异步,也是微任务

4、for...of循环

        for...in  forEach  for 是常规的同步遍历

        而for...of常用于异步遍历

三、宏任务和微任务 

1、宏任务和微任务有哪些 

        宏任务:setTimeOut、setInterval、Ajax、DOM事件

        微任务:Promise、async/await

        微任务的执行时机要比宏任务早

2、为什么微任务比宏任务执行更早

(alert会阻塞js代码执行,也会阻断DOM渲染)

        微任务是在DOM渲染前触发,它是ES6语法规定的

        宏任务是在DOM渲染后触发,它是由浏览器规定的

        在执行promise这种微任务时,由于他是ES6语法规定的,所以不会经过web APIs。而是等待时机,把它先放入micro task queue(微任务队列)里。所以当call stack调用栈清空时,会遍历 micro task queue ,取出所有的微任务挨个执行,再尝试DOM渲染,最后触发Event Loop。

四、JS-Web-API-DOM

1、DOM的本质 

        DOM的本质是HTML文件解析出来的一棵树。

2、DOM节点操作

const h1 = document.getElementById('shop');//通过shop这个id获取当前元素
const h2 = document.getElementsByTagName('div');//通过div这个标签,获取所有div的集合
const h3 = document.getElementsByClassName('.shop');//通过shop这个class属性,获取所有相同元素的集合
const h4 = document.querySelector('div#shop');//通过属性选择器,获取所有相同元素的第一个
const h4 = document.querySelectorAll('div#shop');//通过属性选择器,获取所有相同元素的集合

3、DOM节点的property和attribute

        attribute:修改HTML属性,会改变HTML结构(新增自定义属性等)

        常用的有:setAttribute,getAttribute

        常见用法:

p1.setAttribute('data-name','CLL');
console.log(p1.getAttribute('data-name'));

        property:修改对象属性,或对DOM元素的JS变量进行修改,不会改变HTML结构

        常见用法: 

p1.a = 100, p1.b = 30;

        两者都会引起DOM渲染

        但property只有部分属性可以渲染。如 align、title、className、style,如float、border、top属性又渲染不出来。

        attribute全部都可以渲染出来

4、新增&插入&移动节点

const div1 = document.getElementById('div1');
const div2 = document.getElementById('div2');

const p1 = document.createElement('p');//创造一个p标签节点
p1.innerHTML = '我喜欢你';

div1.appendChild(p1);//将p1节点插入到div1里的末尾元素后面

div2.appendChild(p1);//将p1节点移动到div2中

5、获取子元素列表&获取父元素&删除子元素

console.log(p1.parentNode);//打印p1的父元素
console.log(div2.childNodes);//打印div1的所有子元素
div2.removeChild(p1);//删除p1节点

6、一次插入多个节点,如何优化DOM操作的性能

        DOM查询做缓存

//缓存DOM查询结果
const length = document.getElementsByTagName('p').length;
for(let i = 1; i < length; i++){
    //缓存length,只进行一次DOM查询
}

        将频繁操作改为一次性操作

const item = document.getElementById('item');
//创建一个文档片段
const frag = document.createDocumentFragment();

//for循环,执行插入
for(let i = 1; i < 10; i++) {
    //创建li节点
    const li = document.createElement('li');
    //给li增加文本内容
    li.innerHTML = "我喜欢你";
    //把li插入到文档片段中
    frag.appendChild(li);
}
//上树,把文档片段插入DOM树中
item.appendChild(frag);

四、JS-Web-API-BOM

1、如何识别浏览器的类型

         使用navigator

const ua = navigator.userAgent;

2、location

        location.href        获取网页完整网址

        location.protocol        获取协议

        location.host        获取域名

        location.search        获取传入的参数

        location.hash        获取#后面的内容

        location.patnhame        获取网页路径

3、history

        history.back        后退浏览器页面

        history.forward        前进浏览器页面

五、JS-Web-API-事件

1、手写通用的事件绑定 

//通用的事件绑定,elem是元素本身,type是事件类型,fn是回调函数,selector是事件代理
function bindEvent(elem, type, fn, selector){
    //监听事件
    elem.addEventListener(type, e => {
        const target = event.target;
        if (selector) {
            //有事件代理的情况
            //判断target本身是否为事件代理的元素
            if(target.matches(selector)){
                //是,则执行回调
                fn(target, e);
            }
        } else {
            //没有事件代理的情况
            fn(elem, e);
        }
    })
}

(matches是DOM查询的API,可以查询某个DOM是否匹配某个css选择器)

2、事件冒泡

         事件冒泡:IE的事件流,即事件开始时由最具体的元素(文档嵌套层次最深的那个节点)接收,然后逐级向上传播到较为不具体的节点(document)

        事件捕获:是不具体的节点更早接收到事件,而具体的节点应该最后接收到事件

//通用的事件绑定, elem是元素本身,type是事件类型,fn是回调函数
function bindEvent(elem, type, fn){
    //使用addEventListener模仿事件绑定
    elem.addEventListener(type, fn);
}
//获取元素
const p1 = document.getElementById('p1');
const body = document.body;
//监听事件,关于p1的点击事件
bindEvent(p1, 'click', e => {
    e.stopPropagation();//阻止事件冒泡
    console.log('p1');
})
//关于body的点击事件
bindEvent(body, 'click', e => {
    console.log('body');
})

         由上面代码我们可以看到,当p1点击的时候,会触发p1绑定的click事件,打印p1。但由于引起了事件冒泡,逐渐向上级传播触发到了body的点击事件,便又会打印body。想要阻止这个方法,可以使用stopPropagation阻止事件冒泡,让body点击事件无法触发

3、事件代理/委托

        由于事件会在冒泡阶段向上传播到父节点,因此可以把子节点的监听函数绑定在父节点上,由父节点的监听函数统一处理多个子元素的事件,这种方法叫做事件代理

//获取父节点item
var item = document.querySelector('ul');
//给父节点绑定click事件
item.addEventListener('click', e => {
    //判断当前点击的元素是不是事件代理元素
    if (e.target.matches('li')) {
        //是,则打印当前元素的文本内容
        console.log(e.target.innerHTML);
    } 
});

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值