1. JS有哪些数据类型,它们的区别有哪些?
JS 数据类型分为基本数据类型和引用数据类型。
- 基本数据类型:String、Number、Boolean、Null、Undefined、Symbol、BigInt,共7种,存储在栈内存,值不可变,按值传递。
- 引用数据类型:Object(包含Array、Function、Date等),存储在堆内存,栈内存保存堆内存地址,值可变,按引用传递。
2. 数据类型检测的方式有哪些?
- typeof:检测基本数据类型(null 会被识别为 object),引用类型除 function 外都识别为 object。
- instanceof:检测构造函数的 prototype 是否出现在实例的原型链上,适用于引用类型。
- prototype.toString.call():最准确的检测方式,返回 object 类型 格式字符串,可识别所有数据类型。
- constructor:通过实例的 constructor 属性指向其构造函数来判断。
3. 介绍下Ajax
Ajax 全称 Asynchronous JavaScript and XML(异步 JavaScript 和 XML),是一种在不重新加载整个页面的情况下,与服务器交换数据并更新部分页面的技术。核心是 XMLHttpRequest 对象(或现代的 fetch API),实现异步请求,提升用户体验。流程:创建请求对象→配置请求参数→发送请求→监听状态变化→处理响应数据。
4. 如何判断一个数据是NaN?
- isNaN():ES6 新增方法,仅当参数是 NaN 且类型为 Number 时返回 true,避免了全局 isNaN() 的类型转换问题。
- value !== value:利用 NaN 是唯一不等于自身的值的特性,这是判断 NaN 的可靠方法。
5. null和undefined的区别
- undefined:表示变量已声明但未赋值,或对象属性不存在,是 JS 自动赋予的初始值;typeof undefined 返回 undefined。
- null:表示变量主动赋值为“空值”,代表一个空对象指针;typeof null 返回 object。
- 转换为数值时:Number(undefined) 是 NaN,Number(null) 是 0。
6. 介绍下闭包,在什么场景下使用过?
- 定义:闭包是指有权访问另一个函数作用域中变量的函数,本质是函数作用域链的保留。
- 特性:延长外部函数变量的生命周期,隔离作用域避免变量污染。
- 使用场景:① 封装私有变量(如计数器函数,避免全局变量);② 防抖节流函数的实现;③ 模块化开发中暴露特定方法。
7. 事件委托是什么?如何确定事件源?
- 事件委托:利用事件冒泡机制,将子元素的事件绑定到父元素上,由父元素统一处理。优点是减少事件绑定次数、支持动态添加的子元素。
- 确定事件源:在事件处理函数中,通过 target(触发事件的真实元素)获取事件源,兼容低版本 IE 用 event.srcElement。
8. 本地存储与cookie的区别
| | |
|---|
| | |
| localStorage 永久存储,sessionStorage 会话结束失效 | |
| | |
| | |
9. 简述下ES6的新特性
- 块级作用域:let、const 关键字。
- 箭头函数:简化函数写法,无自己的 this。
- 解构赋值:快速提取数组、对象的属性。
- 模板字符串:反引号 ` 包裹,支持换行和变量插值 ${}。
- 类与继承:class、extends 关键字,简化原型链写法。
- 模块化:import/export 语法。
- 新增数据结构:Set、Map。
- 异步方案:Promise 对象。
- 其他:默认参数、剩余参数 ...、扩展运算符 ... 等。
10. Let、var和const的区别
| | | |
|---|
| | | |
| | | |
| | | |
| | | 声明时必须赋值,且不可修改引用(基本类型不可改,引用类型属性可改) |
11. 数组都有哪些方法
- 增删改查:push()(尾增)、pop()(尾删)、unshift()(头增)、shift()(头删)、splice()(增删改)、slice()(截取)。
- 遍历迭代:forEach()、map()、filter()、reduce()、some()、every()。
- 排序与转换:sort()(排序)、reverse()(反转)、join()(转字符串)、concat()(合并数组)。
- 其他:indexOf()/lastIndexOf()(查找索引)、includes()(判断是否包含)、find()/findIndex()(查找元素/索引)、flat()(扁平化数组)。
12. JSON如何新增和删除键值对
JSON 本质是符合格式的 JS 对象,操作方式与对象一致:
- 新增键值对:key = value 或 obj'key' = value。
- 删除键值对:使用 delete obj.key 或 delete obj'key'。
13. 简述下面向对象
面向对象(OOP)是一种编程思想,核心是封装、继承、多态。
- 封装:将数据和操作数据的方法封装在对象中,隐藏内部细节,暴露公共接口。
- 继承:子类继承父类的属性和方法,实现代码复用。
- 多态:同一方法在不同对象上有不同的表现形式(JS 中通过重写方法实现)。
JS 是基于原型的面向对象语言,没有类的概念(ES6 class 是语法糖)。
14. 普通函数与构造函数的区别
- 命名规范:构造函数首字母通常大写,普通函数首字母小写。
- 调用方式:构造函数用 new 关键字调用,普通函数直接调用。
- 返回值:构造函数默认返回实例对象(手动返回对象会覆盖);普通函数无 return 时返回 undefined。
- this 指向:构造函数中 this 指向新创建的实例;普通函数中 this 指向调用者(全局调用指向 window/global)。
15. 请简述原型,原型链和继承
- 原型(prototype):每个函数都有 prototype 属性,指向原型对象,原型对象包含所有实例共享的属性和方法。
- 原型链:每个实例对象都有 proto 属性,指向其构造函数的 prototype;当访问实例属性时,会依次向上查找原型对象,直到 prototype,这条链式结构就是原型链。
- 继承:JS 中继承基于原型链实现,子类实例的 proto 指向父类的 prototype,从而继承父类的属性和方法;常见方式有原型链继承、构造函数继承、组合继承等。
16. 简述下对Promise的理解以及你在什么情况下使用过
- Promise 理解:Promise 是解决 JS 异步回调地狱的方案,代表一个异步操作的最终完成(或失败)及其结果值。有三种状态:pending(进行中)、fulfilled(已成功)、rejected(已失败),状态一旦改变不可逆转。提供 then()、catch()、finally() 方法链式调用。
- 使用场景:① 异步请求(如 axios 基于 Promise 封装,处理接口请求的成功和失败);② 读取文件(js 中 fs.promises 模块);③ 多个异步操作的串行/并行处理(Promise.all()/Promise.race())。
17. 简述下async的用法
async 用于声明异步函数,返回值是一个 Promise 对象;await 关键字只能在 async 函数中使用,用于等待 Promise 执行完成,暂停函数执行直到 Promise 状态变为 fulfilled 或 rejected。
- 成功时:await 返回 Promise 的成功值;
- 失败时:需用 try/catch 捕获异常,否则会抛出错误。
- 作用:将异步代码以同步的写法呈现,比 Promise 链式调用更简洁。
18. 简述下jQuery
jQuery 是一款轻量级的 JS 库,核心思想是 Write Less, Do More(写得更少,做得更多)。它封装了原生 JS 的 DOM 操作、事件处理、Ajax 请求等功能,解决了浏览器兼容性问题。特点:① 简洁的选择器;② 链式调用;③ 丰富的插件生态;④ 动画效果便捷。但随着 Vue、React 等框架的兴起,jQuery 在现代前端开发中使用逐渐减少。
19. 什么是Sass、Less,为什么使用它们
- Sass/Less:都是 CSS 预处理器,扩展了 CSS 的语法,增加了变量、混合、嵌套、继承等特性,让 CSS 更易维护和复用。Sass 后缀是 .scss(或 .sass),Less 后缀是 .less。
- 使用原因:① 变量:统一管理颜色、字体等样式属性;② 嵌套:模拟 DOM 层级结构,增强代码可读性;③ 混合:复用公共样式片段;④ 继承:减少代码冗余;⑤ 模块化:拆分样式文件,便于维护。
20. JS中call()和apply()方法的区别
call() 和 apply() 都用于改变函数执行时 this 的指向,第一个参数都是 this 要指向的对象。
- 区别:传入参数的方式不同。
- call():第一个参数是 this 指向,后续参数是单个参数列表,用逗号分隔。
例:fn.call(obj, arg1, arg2)
- apply():第一个参数是 this 指向,第二个参数是参数数组(或类数组对象)。
例:fn.apply(obj, arg1, arg2)
21. 为什么会造成跨域?
跨域是指浏览器的同源策略限制,当一个请求的协议、域名、端口三者中任意一个与当前页面不同,就会产生跨域。同源策略是浏览器的安全机制,防止不同源的页面之间随意访问数据,避免 XSS、CSRF 等攻击。
22. this有几种指向?
- 全局环境:this 指向全局对象(浏览器中是 window,js 中是 global)。
- 函数直接调用:非严格模式下 this 指向全局对象,严格模式下 this 是 undefined。
- 对象方法调用:this 指向调用该方法的对象。
- 构造函数调用:this 指向新创建的实例对象。
- call()/apply()/bind() 调用:this 指向传入的第一个参数。
- 箭头函数:没有自己的 this,this 指向箭头函数定义时所在作用域的 this。
- 事件处理函数:this 指向触发事件的 DOM 元素。
23. 请说出三种减少页面加载时间的方式
- 资源压缩:压缩 JS、CSS、HTML 文件,减小文件体积;压缩图片(WebP 格式、图片压缩工具)。
- 资源缓存:设置合理的 HTTP 缓存头(如 Cache-Control、Expires),利用 localStorage 缓存不常变化的静态资源。
- 减少 HTTP 请求:合并 CSS/JS 文件,使用雪碧图合并小图标,采用懒加载加载非首屏资源。
- CDN 加速:将静态资源部署到 CDN 服务器,就近获取资源,提高加载速度。
24. 什么是JSONP,工作原理是什么?它为什么不是真正的Ajax?
- JSONP:是一种跨域请求解决方案,全称 JSON with Padding。
- 工作原理:利用 <script> 标签不受同源策略限制的特性,动态创建 <script> 标签,请求后端接口,后端返回一个函数调用的字符串,函数参数是需要的 JSON 数据,前端提前定义好该函数,从而获取数据。
- 不是真正的 Ajax:Ajax 基于 XMLHttpRequest 对象实现,而 JSONP 基于 <script> 标签的请求,不依赖 XMLHttpRequest,且只支持 GET 请求,不支持 POST 等其他请求方法。
25. 说几种数组去重方式
- 利用 Set:...new Set(arr),简洁高效,ES6 推荐方法。
- 利用 indexOf/includes:遍历数组,判断元素是否已存在于新数组中,不存在则添加。
- 利用 filter + indexOf:filter((item, index) => arr.indexOf(item) === index)。
- 利用对象属性唯一性:将数组元素作为对象的键,避免重复。
26. 简述下深浅拷贝,并说下如何分别实现,以及使用场景
- 浅拷贝:只复制对象的第一层属性,若属性是引用类型,复制的是地址,修改新对象会影响原对象。
- 实现方法:assign()、扩展运算符 {...obj}、数组 slice()/concat()。
- 使用场景:复制只有基本类型属性的简单对象。
- 深拷贝:复制对象的所有层级属性,新对象与原对象完全独立,修改互不影响。
- 实现方法:parse(JSON.stringify(obj))(缺点:无法复制函数、RegExp 等)、递归手写深拷贝、lodash.cloneDeep()。
- 使用场景:复制包含引用类型属性的复杂对象(如嵌套对象、数组)。
27. 为什么JS是弱类型语言
弱类型语言的特点是变量类型不固定,支持隐式类型转换。JS 中变量声明时不需要指定类型,赋值后类型可以随时改变;在运算时,JS 会自动将不同类型的值转换为相同类型再计算(如 1 + '2' = '12')。与之相对的是强类型语言(如 Java),变量类型固定,必须显式转换类型。
28. 怎么转换Less为CSS
- 使用 Less 官方编译器:安装js 后,通过 npm 安装 less 包,执行命令 lessc styles.less styles.css 编译。
- 构建工具集成:在 Webpack/Vite 等构建工具中配置 less-loader,打包时自动将 Less 转换为 CSS。
- 编辑器插件:使用 VS Code 的 Easy LESS 插件,保存 Less 文件时自动生成对应的 CSS 文件。
29. ECharts使用最多的是什么?
ECharts 是百度开源的可视化图表库,使用最多的是各类统计图表的绘制,包括:
- 折线图/柱状图:用于展示数据的趋势和对比。
- 饼图/环形图:用于展示数据的占比情况。
- 地图:用于展示地理相关的数据分布。
- 仪表盘:用于展示关键指标的数值。
核心是通过配置项 option 设置图表的数据源、样式、交互等属性。
30. for循环和map循环有什么区别?
- 返回值:for 循环无返回值,需手动操作数组;map 循环返回一个新数组,新数组元素是原数组元素经过回调函数处理后的结果。
- 功能:for 循环可用于遍历、修改原数组、跳出循环(break/continue);map 循环主要用于映射转换数组,不能中断循环。
- 可读性:map 循环写法更简洁,语义化更强,适合数组的批量转换;for 循环更灵活,适合复杂的遍历逻辑。
31. 请写一个简单的类与继承
// 父类
class Person {
constructor(name, age) {
this.name = name;
this.age = age;
}
sayHello() {
console.log(`我是${this.name},今年${this.age}岁`);
}
}
// 子类继承父类
class Student extends Person {
constructor(name, age, grade) {
super(name, age); // 调用父类构造函数
this.grade = grade;
}
study() {
console.log(`${this.name}在${this.grade}年级学习`);
}
}
// 实例化
const stu = new Student('小明', 12, 6);
stu.sayHello(); // 我是小明,今年12岁
stu.study(); // 小明在6年级学习
32. 同步与异步的区别?阻塞与非阻塞的区别?
同步与异步
- 同步:代码按顺序执行,前一个任务完成后才执行下一个任务,主线程会被阻塞。例:普通函数调用、alert()。
- 异步:任务不会阻塞主线程,发起后继续执行后续代码,任务完成后通过回调/事件通知结果。例:setTimeout、Ajax 请求。
阻塞与非阻塞
- 阻塞:线程执行任务时,必须等待任务完成才能继续执行其他操作,线程处于等待状态。
- 非阻塞:线程执行任务时,若任务未完成,可立即返回去执行其他操作,无需等待,通过轮询或回调获取任务结果。
- 关系:同步≠阻塞,异步≠非阻塞,它们是不同维度的概念(同步异步描述任务的执行顺序,阻塞非阻塞描述线程的状态)。
33. HTTP是什么?有什么特点?
HTTP 全称 HyperText Transfer Protocol(超文本传输协议),是用于在客户端和服务器之间传输数据的应用层协议,基于 TCP/IP 协议。
- 特点:
- 无状态:协议本身不记录客户端的请求状态,每次请求都是独立的(可通过 Cookie/Session 保持状态)。
- 无连接:HTTP 1.0 中,每次请求都要建立新的 TCP 连接,请求完成后断开;HTTP 1.1 支持持久连接(Keep-Alive)。
- 简单快速:请求格式简单,客户端向服务器发送请求方法和路径,服务器返回状态码和数据。
- 灵活:支持多种数据类型(如文本、图片、视频等)。
34. HTTP协议和HTTPS的区别
35. 原型和继承,prototype,call和apply继承的区别
- 原型继承:将子类的 prototype 指向父类的实例,子类实例可继承父类原型上的属性和方法。缺点:父类的引用类型属性会被所有子类实例共享;无法向父类构造函数传参。
- call/apply 继承:在子类构造函数中调用父类构造函数,通过 call()/apply() 改变父类 this 指向子类实例,实现父类实例属性的继承。缺点:无法继承父类原型上的方法。
- 组合继承:结合原型继承和 call/apply 继承,既继承父类实例属性,又继承父类原型方法,是最常用的继承方式。
36. 说几种数组和字符串的方法及他们的作用
数组方法
- map():遍历数组,返回新数组,元素为回调函数处理结果。
- filter():过滤数组元素,返回符合条件的新数组。
- reduce():累计计算数组元素,返回最终结果(如求和、求积)。
- find():返回数组中第一个符合条件的元素。
字符串方法
- split():将字符串按分隔符分割为数组。
- indexOf()/includes():查找子字符串是否存在,返回索引或布尔值。
- substring()/slice():截取字符串的指定部分。
- replace():替换字符串中的指定内容。
37. 箭头函数与普通函数的区别
- this 指向:箭头函数无自己的 this,指向定义时所在作用域的 this;普通函数 this 指向调用者。
- 构造函数:箭头函数不能作为构造函数,不能用 new 调用;普通函数可以。
- 参数:箭头函数没有 arguments 对象,可使用剩余参数 ...args;普通函数有 arguments。
- 原型:箭头函数没有 prototype 属性;普通函数有。
- 写法:箭头函数写法更简洁,适合回调函数;普通函数写法更灵活。
38. 什么是JS内存泄露
内存泄露是指程序中已不再使用的内存没有被及时释放,导致内存占用越来越高,最终影响程序性能甚至崩溃。JS 中常见的内存泄露场景:
- 意外的全局变量(如未声明的变量)。
- 闭包导致的变量未释放。
- 未清除的定时器/事件监听器。
- DOM 元素被删除但仍有引用(如变量保存了已删除的 DOM 节点)。
39. 如何对网站的文件和资源进行优化
- 静态资源优化:压缩 JS/CSS/HTML,图片格式转换(WebP)、图片懒加载、雪碧图合并小图标。
- 资源加载优化:使用 CDN 加速,预加载关键资源(preload),预解析 DNS(dns-prefetch)。
- 代码优化:减少 HTTP 请求,合并文件;删除无用代码(Tree Shaking);延迟加载非首屏脚本。
- 缓存优化:设置强缓存和协商缓存,利用 localStorage 缓存静态数据。
- 服务器优化:启用 Gzip/Brotli 压缩,使用 HTTP/2 协议(多路复用)。
40. 简述Ajax的执行过程以及常见的HTTP状态码
Ajax 执行过程
- 创建 XMLHttpRequest 对象:const xhr = new XMLHttpRequest()。
- 配置请求参数:open(method, url, async)(method:请求方法;url:请求地址;async:是否异步)。
- 设置响应处理函数:onreadystatechange = function() {},监听 readyState 变化。
- 发送请求:send(data)(POST 请求需传递数据)。
- 处理响应:当 readyState === 4 且 status === 200 时,获取响应数据 responseText。
常见 HTTP 状态码
- 2xx 成功:200(请求成功)、201(创建资源成功)。
- 3xx 重定向:301(永久重定向)、302(临时重定向)、304(资源未修改,使用缓存)。
- 4xx 客户端错误:400(请求参数错误)、401(未授权)、403(禁止访问)、404(资源不存在)。
- 5xx 服务器错误:500(服务器内部错误)、503(服务器不可用)。
41. 预加载和懒加载的区别,预加载在什么时间合适
区别
- 预加载:提前加载未来可能需要的资源(如图片、JS 文件),加载完成后缓存,当用户需要时直接从缓存读取,提升体验。主动加载,会增加首屏加载时间。
- 懒加载:延迟加载非首屏资源,只有当资源进入可视区域时才加载,减少首屏加载时间,提升页面加载速度。被动加载,适用于图片、视频等大量静态资源。
预加载合适的时间
预加载应在首屏资源加载完成后进行,避免抢占首屏资源的带宽,影响首屏渲染速度。可通过 window.onload 事件触发,或在页面空闲时(requestIdleCallback)执行。
42. jQuery选择器有哪些?
jQuery 选择器基于 CSS 选择器,分为以下几类:
- 基本选择器:ID 选择器($('#id'))、类选择器($('.class'))、标签选择器($('div'))、通配符选择器($('*'))。
- 层级选择器:后代选择器($('parent child'))、子元素选择器($('parent > child'))、相邻兄弟选择器($('prev + next'))。
- 过滤选择器:基本过滤(:first、:last、:eq(index))、内容过滤(:contains(text))、可见性过滤(:visible、:hidden)。
- 属性选择器:$('attr')、$('attr=value')。
43. jQuery插入节点的方法
- 内部插入:
- append():在元素内部末尾插入节点。
- prepend():在元素内部开头插入节点。
- 外部插入:
- after():在元素外部后面插入节点。
- before():在元素外部前面插入节点。
- 替换节点:replaceWith():用新节点替换原节点。
- 包裹节点:wrap():用指定节点包裹每个匹配元素。
44. Get和Post区别
45. 什么是CSRF攻击
CSRF 全称 Cross-Site Request Forgery(跨站请求伪造),是一种网络攻击手段。攻击者诱导用户在已登录目标网站的情况下,访问恶意网站,利用用户的登录状态向目标网站发送伪造的请求,从而执行非用户意愿的操作(如转账、修改密码)。防御措施:① 验证 Referer 字段;② 使用 CSRF Token;③ 验证码验证。
46. 如何遍历一个多维数组?
- 递归遍历:遍历数组元素,若元素是数组则递归调用遍历函数,否则处理元素。
function traverse(arr) {
arr.forEach(item => {
if (Array.isArray(item)) {
traverse(item);
} else {
console.log(item);
}
});
}
- 扁平化后遍历:用 flat() 方法将多维数组扁平化为一维数组,再遍历。
const arr = [1, [2, [3, 4]]];
arr.flat(Infinity).forEach(item => console.log(item));
47. Axios的特性?
Axios 是基于 Promise 的 HTTP 客户端,支持浏览器和 Node.js,核心特性:
- 支持 Promise API,可链式调用。
- 拦截请求和响应(请求拦截器处理 token,响应拦截器统一处理错误)。
- 转换请求和响应数据(如自动转换 JSON 数据)。
- 取消请求。
- 防止 CSRF 攻击。
- 客户端支持防御 XSRF。
- 支持多种请求方法(GET、POST、PUT、DELETE 等)。
48. 在地址栏输入一个URL,到页面呈现出来,中间发生了什么?
- DNS 解析:将域名转换为对应的 IP 地址。
- 建立 TCP 连接:客户端与服务器通过三次握手建立连接(HTTP/1.1 默认为持久连接)。
- 发送 HTTP 请求:客户端向服务器发送请求行、请求头、请求体。
- 服务器处理请求:服务器解析请求,处理业务逻辑,生成响应数据。
- 服务器返回响应:服务器向客户端发送响应行、响应头、响应体(HTML 等资源)。
- 关闭 TCP 连接:通过四次挥手关闭连接(若开启 Keep-Alive 则保持连接)。
- 浏览器解析渲染页面:
- 解析 HTML 生成 DOM 树;
- 解析 CSS 生成 CSSOM 树;
- 结合 DOM 树和 CSSOM 树生成渲染树;
- 布局(Layout):计算元素的位置和大小;
- 绘制(Paint):将渲染树绘制到屏幕上。
49. 异步操作的解决方案
- 回调函数:最基础的方案,将异步操作的结果处理逻辑传入回调函数,但容易导致回调地狱。
- Promise:解决回调地狱,通过 then()/catch() 链式调用,支持多个异步操作的串行/并行处理。
- async/await:基于 Promise 的语法糖,以同步写法实现异步操作,代码更简洁易读。
- Generator 函数:通过 yield 暂停函数执行,next() 恢复执行,可实现异步流程控制(较少使用)。
50. map和forEach的区别
51. TCP和UDP的区别
52. BOM和DOM的区别
- DOM:全称 Document Object Model(文档对象模型),是 HTML/XML 文档的编程接口,将文档解析为树形结构,提供操作元素、属性、事件的方法(如 getElementById())。核心是 document 对象。
- BOM:全称 Browser Object Model(浏览器对象模型),是与浏览器窗口交互的接口,提供操作浏览器窗口、地址栏、历史记录等的方法(如 open()、location.href)。核心是 window 对象,DOM 是 BOM 的一部分。
53. 简述下Git操作
Git 是分布式版本控制系统,常用操作:
- 初始化仓库:git init。
- 克隆仓库:git clone <url>。
- 文件操作:git add <file>(添加到暂存区)、git commit -m "message"(提交到本地仓库)。
- 分支操作:git branch(查看分支)、git branch <name>(创建分支)、git checkout <name>(切换分支)、git merge <name>(合并分支)。
- 远程操作:git remote add origin <url>(关联远程仓库)、git push -u origin master(推送代码)、git pull(拉取代码)。
- 版本回退:git log(查看提交记录)、git reset --hard <commit-id>(回退到指定版本)。
54. 什么是Node.js?
Node.js 是基于 Chrome V8 引擎的 JavaScript 运行时环境,让 JS 可以脱离浏览器运行在服务器端。特点:
- 非阻塞 I/O:处理高并发请求性能优异。
- 事件驱动:基于事件循环机制,异步处理请求。
- 丰富的模块生态:通过 npm 管理大量第三方模块。
- 跨平台:支持 Windows、Linux、macOS 等系统。
用途:搭建后端服务器、开发 CLI 工具、构建前端工程化工具(如 Webpack)。
55. 遍历数组,遍历对象,遍历字符串的方法都有哪些?哪些可以打断?
遍历数组
- 可打断的方法:for 循环(break/continue)、..of 循环(break/continue)。
- 不可打断的方法:forEach()、map()、filter()、reduce()。
遍历对象
- ..in:遍历对象的可枚举属性(包括原型链上的属性),可通过 break 打断。
- keys()/Object.values()/Object.entries():返回数组后遍历,可结合 for 循环打断。
遍历字符串
- 可打断的方法:for 循环、..of 循环。
- 不可打断的方法:split('').forEach()。
56. 回流和重绘
- 回流(Reflow):当元素的布局属性发生变化(如宽高、位置、DOM 结构),浏览器需要重新计算元素的几何属性和位置,重新构建渲染树,这个过程叫回流。回流代价较高,会触发重绘。
- 重绘(Repaint):当元素的样式属性发生变化(如颜色、背景色),但不影响布局时,浏览器只需重新绘制元素外观,这个过程叫重绘。重绘代价低于回流。
- 触发回流的操作:添加/删除 DOM 元素、改变元素尺寸、改变窗口大小、offsetWidth/offsetHeight 等属性的读取。
- 优化:减少回流次数(如批量修改样式、使用 documentFragment 批量添加 DOM)。
57. 节流和防抖
- 防抖(Debounce):触发事件后,在指定时间内没有再次触发事件,才执行回调函数;若在指定时间内再次触发,则重新计时。适用于搜索框输入联想、窗口大小调整等场景。
function debounce(fn, delay) {
let timer = null;
return function(...args) {
clearTimeout(timer);
timer = setTimeout(() => fn.apply(this, args), delay);
};
}
- 节流(Throttle):触发事件后,每隔指定时间执行一次回调函数,在指定时间内多次触发只执行一次。适用于滚动加载、鼠标移动、按钮点击等场景。
function throttle(fn, interval) {
let lastTime = 0;
return function(...args) {
const now = Date.now();
if (now - lastTime >= interval) {
fn.apply(this, args);
lastTime = now;
}
};
}
58. 宏任务和微任务
宏任务和微任务是 JS 异步任务的分类,事件循环中执行顺序为:先执行同步代码→执行所有微任务→执行一个宏任务→再执行所有微任务,以此循环。
- 宏任务(Macrotask):执行时间较长的任务,包括 setTimeout、setInterval、I/O、UI 渲染、script 整体代码。
- 微任务(Microtask):执行时间较短的任务,包括 then()/catch()/finally()、async/await、queueMicrotask()、MutationObserver。
59. 什么是装饰器?
装饰器(Decorator)是一种设计模式,用于在不修改原函数/类代码的前提下,动态地为其添加额外功能。ES7 中提出了装饰器语法(目前是提案,需通过 Babel 编译)。
- 类装饰器:用于装饰类,修改类的行为。
- 方法装饰器:用于装饰类的方法,修改方法的执行逻辑。
- 用途:日志记录、性能监控、权限校验等。例如:用装饰器记录函数的执行时间。
60. 什么是迭代器?
迭代器(Iterator)是一种接口,为不同的数据结构提供统一的遍历机制。任何数据结构只要部署了 Iterator 接口,就可以通过 for...of 循环遍历。
- 迭代器的特性:有一个 next() 方法,每次调用返回一个对象 { value: 当前值, done: 是否遍历完成 }。
- 原生支持迭代器的数据结构:数组、字符串、Set、Map。
- 自定义迭代器:通过 iterator 属性为对象部署迭代器接口。
61. 什么是前端微服务?
前端微服务是借鉴后端微服务的思想,将大型前端应用拆分为多个独立的、可独立开发、测试、部署的小型应用,每个小型应用称为一个“微应用”。
- 核心特点:
- 独立部署:每个微应用可单独发布,不影响其他微应用。
- 技术栈无关:不同微应用可使用不同的前端框架(如 Vue、React)。
- 共享基础资源:共享公共组件、工具库、状态管理等。
- 运行时集成:通过主应用(基座)加载微应用,实现页面跳转和通信。
- 实现方案:基于 qiankun、single-spa 等框架。