Javascript
1、解释事件循环(Event Loop)机制
参考链接:JS事件循环(Event Loop)机制-CSDN博客
答:JavaScript 的事件循环(Event Loop)是 JavaScript 运行机制的核心部分,它负责处理代码的执行、事件的处理以及异步操作的调度。
事件循环会不断检查调用栈是否为空:
-
如果调用栈为空,它会首先检查微任务队列是否有待执行的函数,如果有,就执行这些微任务。
-
执行完所有微任务后,它会检查宏任务队列,并执行其中的一个任务(通常是时间最接近的任务)。
-
一旦宏任务执行完毕,再次检查微任务队列,如此循环往复。
2. 什么是闭包?闭包有什么作用和使用场景?
参考链接:Javascript闭包-CSDN博客
答:闭包是指在JavaScript中,一个内部函数能够访问其外部函数的作用域中的变量,即使外部函数已经执行完毕。闭包由函数及其词法环境组成,内部函数持有外部作用域的变量引用。
闭包的使用场景 :
-
防抖和节流:在处理高频事件(如窗口缩放、滚动)时,通过闭包保存计时器ID,避免函数被频繁调用。
-
模块开发:使用 IIFE (立即执行函数表达式)返回包含私有方法的对象,实现模块的封装和私有化。
-
循环陷阱处理:在循环中使用闭包绑定正确的循环变量值,防止异步操作中使用错误的循环变量。
-
高阶函数:在柯里化操作中保持参数状态,通过闭包动态生成函数。
3. 解释原型链和继承。
参考链接:JS原型链和继承-CSDN博客
答:每个JS对象都有一个隐藏的__proto__属性,指向它的原型对象,当访问对象的属性时,如果对象本身没有该属性,就会沿着原型链向上查找,原型对象也有自己的原型,最终指向null,形成一条链式结构。构造函数通过prototype属性关联原型对象,使用new创建实例时,实例的__proto__会自动指向构造函数的prototype。
4. 如何实现深拷贝?
答:(1)使用JSON.stringify()和JSON.parse(),不能复制函数、Symbol、undefined等特殊类型,会丢失对象的constructor信息.(2)手动遍历对每个对象,递归拷贝。(3)使用第三方库,Lodash的_.cloneDeep(),jQuery的$.extend(true, {}, obj)。(4)MessageChannel API(处理特殊对象),可以复制函数等JSON方法不能处理的对象
5. 解释this关键字的指向。
答:默认指向全局对象(浏览器中为window),在对象中指向调用该方法的对象,使用call/apply/bind函数可强制指定this指向,使用new绑定(构造函数)指向新创建的实例对象,箭头函数继承外层作用域的this(词法作用域)。
6. 解释call、apply、bind的区别。
答:call
/apply会
立即执行原函数,bind需要
返回绑定后的新函数(需手动调用);call的
参数逐个传递(逗号分隔),apply的
参数通过数组传递,bind
支持分步传参(绑定时可预置部分参数);call
/apply
仅临时修改本次调用的this
指向,bind是
永久绑定this
(除非用new
调用)。
方法 | 执行时机 | 参数形式 | this绑定效果 |
---|---|---|---|
call | 立即执行 | 逐个参数 | 临时修改 |
apply | 立即执行 | 数组 | 临时修改 |
bind | 延迟执行 | 支持分步传参 | 永久绑定(非new) |
7. Promise是什么?如何实现一个Promise?
答:Promise 是一种用于处理异步操作的对象,它可以将回调函数变成链式调用,使代码更加清晰和优雅。Promise 有三种状态:pending(进行中)、fulfilled(已成功)和 rejected(已失败)。状态一旦改变,就不会再变。实现一个基础版的 Promise 类需要包含以下几个部分:
-
构造函数:接受一个执行器函数,该函数包含两个参数:resolve 和 reject。
-
状态:Promise 有三个状态,初始状态为 pending。
-
值:用于存储成功或失败的值。
-
回调队列:用于存储 then 方法传入的回调函数。
8. async/await的工作原理是什么?
答:async 和 await 是 JavaScript 中用于处理异步操作的关键字。它们基于 Promise,提供了一种更简洁和直观的方式来编写异步代码,使其看起来像同步代码。
async 关键字用于声明一个异步函数。异步函数会隐式地返回一个 Promise 对象,其结果由函数的返回值决定。
await 关键字用于等待一个 Promise 对象的结果。它只能在 async 函数内部使用。await 会暂停函数的执行,直到 Promise 对象完成,并返回其结果。
9. 解释防抖(debounce)和节流(throttle)以及它们的应用场景。
答: 防抖 (Debounce)和 节流 (Throttle)是两种用于优化高频事件处理的JavaScript技术,主要用于减少函数执行的频率,从而节省资源并提高应用性能。
防抖的核心思想是延迟执行,只有在事件触发的频率降低到一定程度后才会执行。如果在设定的延迟时间内再次触发事件,则会重新计时,直到用户停止触发事件一段时间后才会执行。比如:
- 搜索输入框输入停止一段时间后再发送请求,避免因频繁输入而频繁请求
- 窗口的调整可以在窗口大小稳定后进行触发
- 表单验证中可以在输入停止一段时间后再进行验证
节流的核心思想是保证在特定时间内至少执行一次函数,不论事件触发的频率如何。节流适用于那些需要定期执行但不需要过于频繁的场景,例如:
- 在滚动事件中,如果不加限制地执行函数会导致性能问题。使用节流可以保证在每次滚动时至少执行一次函数,避免过于频繁的执行。
- 在用户快速点击按钮时,防止重复提交表单或执行不必要的操作。使用节流可以确保在特定时间内最多执行一次函数。
- 在用户连续输入时,使用节流可以保证在特定时间内最多执行一次输入处理函数,避免过于频繁的输入处理。
10. ES6有哪些新特性?
答:块级作用域变量(let/const)、箭头函数、模板字符串、解构赋值、类和模块系统等
11. 解释箭头函数和普通函数的区别。
答:箭头函数与普通函数的核心区别集中在this绑定规则、构造函数使用限制和语法特性三个方面。箭头函数没有独立的this值,继承外层作用域的this;无法作为构造函数使用;语法更简洁但缺少arguments对象和prototype属性。
特征 | 箭头函数 | 普通函数 |
---|---|---|
this绑定 | 静态继承外层作用域 | 动态绑定调用对象 |
构造函数支持 | ❌ | ✅ |
arguments对象 | ❌(需用剩余参数) | ✅ |
显式绑定this | ❌ | ✅ |
匿名函数占比 | 高 | 可变(具名/匿名) |
12. 解释let、const、var的区别。
答:var:仅在声明它的函数内部有效,或在全局作用域中有效,声明被提升到作用域顶部,初始化值为 undefined,
允许重复声明(可能引发意外覆盖),声明后值可修改,在全局作用域声明时,变量会成为 window
对象的属性。
let:在块级作用域({}
内有效),在 if
、for
等代码块内部声明的变量不会泄漏到外部,存在暂时性死区(TDZ)声明前访问会报错(ReferenceError),但声明本身仍被提升,禁止重复声明,声明后值可修改,不会添加到 window
对象。
const:在块级作用域({}
内有效),在 if
、for
等代码块内部声明的变量不会泄漏到外部,存在暂时性死区(TDZ)声明前访问会报错(ReferenceError),但声明本身仍被提升,禁止重复声明,声明后值不可修改,不会添加到 window
对象。
特性 | var | let | const |
---|---|---|---|
作用域 | 函数作用域 | 块级作用域 | 块级作用域 |
变量提升 | ✅(值为 undefined ) | ✅(TDZ 报错) | ✅(TDZ 报错) |
重复声明 | 允许 | 禁止 | 禁止 |
重新赋值 | 允许 | 允许 | 禁止 |
声明时初始化要求 | 可选 | 可选 | 必需 |
全局作用域绑定到 window | ✅ | ❌ | ❌ |
13. 如何判断一个变量的类型?
参考链接:在 JavaScript 中,判断变量类型有多种方法-CSDN博客
答:typeof、instanceof、Object.prototype.toString、Array.isArray()
方法 | 适用场景 | 局限性 |
---|---|---|
typeof | 基本类型、函数 | null 返回 "object" |
instanceof | 检测对象实例(如 Array , Date ) | 不适用于基本类型 |
Object.prototype.toString | 所有类型(最全面) | 需手动提取类型字符串 |
Array.isArray() | 仅检测数组 | 仅适用于数组 |
14. 解释跨域问题以及解决方案(如CORS、JSONP等)。
答:浏览器出于安全考虑,对于同源的请求通过,对于不是同源的请求会限制,这就是浏览器的 同源策略,对于不满足浏览器浏览器同源策略出现的开发问题,就是跨域问题。
使用CORS时,浏览器只允许获取默认的响应头,像上文代码中的标头With-Requested-Key,即便我们可以通过浏览器的调试器查看,也无法通过代码去获取,这时候就需要后台通过Access-Control-Expose-Header进行暴露。
使用.JSONP时,通过script标签没有跨域限制的特性,进行资源的请求和获取,需要目标服务器进行配合,且仅支持get请求。
15. 解释前端模块化(CommonJS、AMD、ES6 Module)。
答:CommonJS:
- 同步加载模块,适用于Node.js服务端环境
- 通过
require()
导入、module.exports
导出 - 模块加载后会被缓存,多次
require
返回相同对象 - 浏览器端同步加载会阻塞渲
AMD规范:
- 异步加载,专为浏览器设计(如RequireJS)
- 使用
define()
定义模块、require()
异步加载 - 依赖前置声明,提前加载所有依赖
ES6 Module:
- 原生支持,静态分析依赖关系
- 通过
import/export
语法实现 - 输出的是值的引用(CommonJS是值拷贝)
特性 | CommonJS | AMD | ES6 Module |
---|---|---|---|
加载方式 | 同步 | 异步 | 静态编译 |
适用环境 | 服务端 | 浏览器 | 全平台 |
输出形式 | 值拷贝 | 动态绑定 | 实时引用 |
语法 | require | define | import |