首页
学习
活动
专区
圈层
工具
发布
社区首页 >专栏 >26秋招前端面试题系列 | JS 进阶篇

26秋招前端面试题系列 | JS 进阶篇

作者头像
爱学英语的程序媛
发布2026-09-15 11:15:29
发布2026-09-15 11:15:29
10
举报

一、异步编程

1. 事件循环(Event Loop)

大家都知道,我们的js是单线程的,无法并行处理多个任务,因此,为了提高CPU的利用率,javascript也是推出了事件循环这一解决办法。

核心原理:JavaScript 是单线程语言,事件循环是处理异步操作的机制,负责协调宏任务与微任务的执行顺序。

1)、宏任务(Macro Task): 包含script(整体代码)、setTimeoutsetIntervalsetImmediate(Node)、I/O 操作、UI 渲染(浏览器)等。 特点:执行完一个宏任务后,会先清空微任务队列,再执行下一个宏任务。

2)、微任务(Micro Task): 包含Promise.then/catch/finallyasync/await(本质是 Promise 的语法糖)、process.nextTick(Node,优先级高于其他微任务)、MutationObserver(浏览器)等。 特点:微任务队列的执行优先级高于宏任务,同一轮事件循环中,所有微任务会在宏任务之间被 “插队” 执行。

那宏任务和微任务在执行过程中遵循怎样的顺序呢?接下来一起看一看。

  • 执行同步代码(属于首个宏任务);
    1. 清空当前微任务队列(按顺序执行所有微任务);
    2. 执行下一个宏任务,重复步骤 

2、Node 与浏览器的差异

1)、浏览器:宏任务队列简单,按 “执行一个宏任务→清空微任务” 循环。

2)、Node:宏任务队列分为 6 个阶段(按顺序执行):

timers(处理setTimeout/setInterval)→pending callbacks(处理延迟的 I/O 回调)→idle, prepare(内部阶段)→poll(处理 I/O 回调,可能阻塞)→check(处理setImmediate)→close callbacks(处理关闭回调,如socket.on('close'))。 微任务在每个宏任务阶段结束后执行,且process.nextTick优先级高于其他微任务。

3、Promise

所谓promise,它是用于解决异步编程的回调地狱问题,代表一个异步操作的最终完成(或失败)及结果值。

1)、状态变化

  • 初始状态:pending(等待中);
  • 只能转化为:fulfilled(成功,调用resolve())或rejected(失败,调用reject());
  • 状态一旦改变(pending→fulfilledpending→rejected),不可逆

2)、链式调用:

  • Promise.then()返回一个新的 Promise(非原 Promise),因此可链式调用。 后一个then的回调参数是前一个then的返回值(若返回非 Promise 值,会被包装为fulfilled状态的 Promise;若返回 Promise,则等待其状态变化)。 3)、异常捕获
    • 通过catch()捕获链式调用中的任意错误(包括reject和代码报错);
    • then(onFulfilled, onRejected)的第二个参数也可捕获错误,但优先级低于catch(且无法捕获onFulfilled中的错误);
    • 错误会 “冒泡”,若未捕获,会触发全局unhandledrejection事件。

4. async/await

补充一个问题:为什么要引入async/await的概念?

所谓async/await,它其实是Promise 的语法糖,使异步代码写法更接近同步代码,解决回调地狱。

async:修饰的函数返回值必为 Promise(若返回非 Promise 值,会自动包装为fulfilled状态的 Promise);

await:只能在async函数中使用,后跟一个 Promise,会暂停函数执行,等待 Promise 状态变为fulfilled后,继续执行并返回结果;若 Promise 被rejected,则抛出异常。

  • 错误处理: 使用try/catch捕获await的异常(替代 Promise 的catch):

看下面这段代码:

代码语言:javascript
复制
asyncfunctionfetchData(){
try{
const res =awaitfetch('url');// 若fetch失败,直接进入catch
const data =await res.json();
return data;
}catch(err){
console.error('出错了:', err);
}
}
  • 回调地狱解决方案: 回调地狱源于多层嵌套的异步回调,async/await通过线性代码结构替代嵌套,使逻辑更清晰:

再看下面这段代码:

// 回调地狱(嵌套)

ajax1(()=>{ ajax2(()=>{ ajax3(()=>{/* ... */}); }); }); // async/await(线性) asyncfunctionrun(){ awaitajax1(); awaitajax2(); awaitajax3(); }

5. DOM 节点操作方法

1)、创建节点

  • document.createElement(tagName):创建元素节点(如div);
  • document.createTextNode(text):创建文本节点;
  • document.createDocumentFragment():创建文档片段(批量插入节点时使用,减少重绘)。

2)、删除节点:

  • parentNode.removeChild(childNode):通过父节点删除子节点(返回被删除节点);
  • node.remove():直接删除节点(IE 不兼容)。

3)、修改节点:

  • 内容修改:node.innerHTML(HTML 内容)、node.textContent(纯文本);
  • 属性修改:node.setAttribute(name, value)node.removeAttribute(name)
  • 样式修改:node.style.cssProperty(如node.style.color = 'red');
  • 移动节点:parentNode.appendChild(childNode)(若节点已存在,会移动位置)、parentNode.insertBefore(newNode, referenceNode)(插入到参考节点前)。

6. DOM 事件流

事件流描述事件在 DOM 树中的传播路径,分为三个阶段(W3C 标准):

补充一个问题:还有其他的标准吗?

  1. 捕获阶段(Capture Phase): 事件从最顶层的window向下传播至目标节点的父节点(由外到内)。
  2. 目标阶段(Target Phase): 事件到达目标节点本身,触发目标节点的事件处理函数。
  3. 冒泡阶段(Bubbling Phase): 事件从目标节点的父节点向上传播至最顶层的window(由内到外)。

示例:点击div内部的button,事件流为: window → document → body → div(捕获)→ button(目标)→ div(冒泡)→ body → document → window

7. 事件委托

原理:利用事件冒泡机制,将子元素的事件监听委托给父元素(或更上层元素),由父元素统一处理。

  • 优势
    • 减少事件监听器数量(优化性能);
    • 自动支持动态添加的子元素(无需重新绑定事件)。
  • 应用场景
    • 列表项(li)的点击事件(如动态渲染的列表);
    • 表格单元格(td)的交互事件等。

看下面这段代码:

代码语言:javascript
复制

const ul =document.querySelector('ul');
ul.addEventListener('click',(e)=>{
if(e.target.tagName==='LI'){// 判断事件源是否为目标子元素
console.log('点击了li:', e.target.textContent);
}
});

在上面的代码中,我们可以看到子元素的点击事件其实是父元素来处理的。这就涉及到了事件委托。想一想,在日常的开发中,还有哪些场景会涉及到事件委托?

8. 阻止事件冒泡与默认行为

  • event.stopPropagation(): 阻止事件在冒泡阶段向上传播(不影响当前节点的其他事件处理函数,也不阻止默认行为)。 注意:stopImmediatePropagation()会同时阻止事件传播和当前节点后续的事件处理函数执行。
  • event.preventDefault(): 阻止事件的默认行为(如a标签跳转、表单提交、右键菜单弹出等),但不影响事件冒泡
  • 区别总结: 操作阻止冒泡?阻止默认行为?stopPropagation()是否preventDefault()否是

9. 事件总线(EventBus)

核心作用:实现跨组件(或模块)的通信,解耦事件的发送者和接收者。

1)、实现原理: 基于发布 - 订阅模式(Pub/Sub),维护一个事件注册表(键为事件名,值为回调函数数组),提供三个核心方法:

  • on(eventName, callback):订阅事件,注册回调;
  • emit(eventName, ...args):发布事件,触发所有订阅该事件的回调(传入参数);
  • off(eventName, callback):取消订阅,移除指定回调(若未传callback,移除该事件所有回调 来看下面这段代码:

class  EventBus

代码语言:javascript
复制
{
constructor(){
this.events={};// 事件注册表:{ 'event1': [cb1, cb2], ... }
}

on(event, cb){
if(!this.events[event])this.events[event]=[];
this.events[event].push(cb);
}

emit(event,...args){
if(this.events[event]){
this.events[event].forEach(cb=>cb(...args));
}
}

off(event, cb){
if(!this.events[event])return;
if(cb){
this.events[event]=this.events[event].filter(fn=> fn !== cb);
}else{
this.events[event]=[];// 移除该事件所有回调
}
}
}

// 使用
const bus =newEventBus();
bus.on('msg',(data)=>console.log('收到:', data));
bus.emit('msg','Hello EventBus');// 输出:收到:Hello EventBus

10. 跨域问题及解决方案

跨域:浏览器的同源策略(协议、域名、端口三者一致为同源)限制不同源的页面之间的资源交互(如 AJAX 请求)。

那我们怎样去解决跨域呢?本文将介绍几种常见的方案:

1)、CORS(跨域资源共享) 原理:服务器通过 HTTP 响应头告知浏览器允许跨域请求。 实现:服务器端设置响应头:

  • 具体的配置主要有以下三个:
代码语言:javascript
复制
Access-Control-Allow-Origin
代码语言:javascript
复制
:https://example.com  # 允许的源(*表示允许所有)
Access-Control-Allow-Methods:GET, POST, PUT     # 允许的方法
Access-Control-Allow-Headers:Content-Type       # 允许的请求头

优势:支持所有 HTTP 方法,安全性高;缺点:需服务器配合。

2)、JSONP 原理:利用script标签不受同源策略限制的特性,通过动态创建script标签加载跨域脚本,脚本执行时调用预设的回调函数传递数据。 实现:

// 客户端 functionhandleData(data){console.log('JSONP数据:', data);} const script =document.createElement('script'); script.src='https://api.example.com/data?callback=handleData';// 服务器返回:handleData({...}) document.body.appendChild(script);

优势:兼容性好(支持老浏览器);缺点:仅支持 GET 方法,安全性低(可能注入恶意代码)。

3)、使用代理服务器 原理:同源策略仅限制浏览器,服务器之间无跨域限制。通过同源的代理服务器转发请求(浏览器→代理服务器→目标服务器)。 实现(以 webpack-dev-server 为例):

// webpack.config.js module.exports={ devServer:{ proxy:{ '/api':{// 匹配以/api开头的请求 target:'https://api.example.com',// 目标服务器 changeOrigin:true,// 欺骗目标服务器,使其认为请求来自代理服务器 pathRewrite:{'^/api':''}// 移除/api前缀 } } } };

优势:前端无需修改代码,支持所有 HTTP 方法;缺点:需额外配置代理服务。

好啦,本文为大家详细介绍了JS进阶相关的基础知识,基本是覆盖了开发中大多数的场景,感谢你的阅读,我们下期再见~

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2025-09-22,如有侵权请联系 cloudcommunity@tencent.com 删除
目录
  • 一、异步编程
  • 1. 事件循环(Event Loop)
  • 大家都知道,我们的js是单线程的,无法并行处理多个任务,因此,为了提高CPU的利用率,javascript也是推出了事件循环这一解决办法。
  • 3、Promise
  • 4. async/await
  • 补充一个问题:为什么要引入async/await的概念?
  • 5. DOM 节点操作方法
  • 6. DOM 事件流
  • 7. 事件委托
  • 8. 阻止事件冒泡与默认行为
  • 9. 事件总线(EventBus)
  • 10. 跨域问题及解决方案
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档