前端面试题 口语化复述解答(从2025.3.8 开始频繁更新中)
背景
看了很多面试题及其答案。但是过于标准化,一般不能直接用于回复面试官,这里我将总结一系列面试题,用于自我复习也乐于分享给大家,欢迎大家提供建议,我必不断完善之。
Javascript
ES6
1. var let const 的区别:
它们均是变量声明的关键字,其中let const是ES6新增关键字。引入这两个关键字的原因在于var没有块级作用域所导致变量滥用的问题。
- var自带变量提升,js编译的时候会将所有用var声明的变量语句提升到整个作用域首位,所以在原有声明前引用该变量不会报错。
- 而let const在变量声明前引用则会报错,原因即此二者无法变量提升导致存在暂时性死区,即从当前作用域的首部开始到变量声明前的上下文无法引用该变量
- var 滥用原因还有可以多次声明同一标识符,这是let const无法做到的。并且由于var 没有块级作用域,用var声明了一个标识符后 在其块级作用域后又重新声明一次则会导致原值被替换为新值。但是var是拥有函数作用域的,所以在函数中重新声明该标识符,在函数作用域外的值则不会被替换,但是如果没用var声明而只是赋值则会被替换。
- let 和 var的值是可以修改的,const 声明的变量对应的地址中的值是不能修改,但是如果当期变量是对象的话,其声明变量的地址中的值是一个指针,指向堆中的值,此时我们可以修改堆中的值(即const变量修改对象的属性是生效的)
- 根据最佳实践来说,尽量能用const就不用let,能用let就不用var
2. Promise 是什么?为什么要使用Promise?它的原理是什么?
Promise 名为期约,是 ES6新引入的一种JavaScript异步解决方案。它用于解决ES6之前连续多次异步操作时传统异步编程的多层回调嵌套的问题,俗称回调地狱。通过连续的then或catch的链式调用,降低了编码的难度同时又增加了代码的可读性。
// ES6以前的异步编程
doFirstThing(data,function (err1, data1) {
if(err1) {
...
return
}
doSecondThing(data1, function (err2, data2){
if(err2) {
...
return
}
doThrdThing(data2, function (err3, data3) {
if(err3){
...
return
}
...
})
})
})
// Promise异步编程
doFirstThing(data).then((data) => doSecondThing(data)).then((data) => doThirdThing(data)).catch((err) => {}).finally(() => {...})
- 基本用法
Promise是一个对象,它有三种状态。即 Pending (进行中)Fulfilled (已完成的, 即成功状态) Rejected (已拒绝的,即失败状态), 当我们生成一个Promise的时候, 状态只能有一种改变,即从Pending-> Fulfilled或从Pending->Rejected。此三者是在其构造函数中进行处理,当我们 new 一个 Promise的时候,需要传入一个函数, 拥有 resolve和 reject方法,通过调用resolve(data) 表示成功处理数据状态,转到链式流程中的then方法中进行后续处理, 通过调用reject(err)表示处理期约失败的数据状态,最后还有一个finnally执行的回调,不管处理成功还是失败都会执行此回调。但其实then也能处理失败的回调,用于局部错误处理。通过then 使用第二个回调函数的参数即代表失败拒绝状态回调
new Promise((resolve, reject) => {
...
resolve(data)
...
reject(err)
}).then((data) => { ... }, (err) => { ... }).catch((err) => {})
但一般我们使用catch来全局处理err,这样语义化分工更易于理解,并且不需要每次then中使用第二个参数控制err更加方便处理。
- 静态方法
另外 Promise作为构造函数包含以下6种静态方法
Promise.resolve
Promise.reject
Promise.all
Promise.allSettled
Promise.race
Promise.any
a. 其中Promise.resolve和Promise.reject返回的分别是成功和失败的Promise对象,等价于前文new Promise((resolve, reject) => {}) 分别调用resolve和reject函数
b. Promise.all([promise1, promise2, promise3]) 并行执行多个Promise, 当所有promise处理成功时即返回一个处理结果数组,依次为传入promise数组的处理结果。当某个Promise拒绝时,直接返回拒绝状态的err。
c. Promise.allSettled([promise1, promise2, promise3]) 格式与all类似,但它会等待所有promise返回结果,最后生成一个对象
//成功
{ status: 'fulfilled', value: results }
//失败
{ status: 'rejected', reason: err }
d. Promise.race([promise1, promise2, promise3]) 为Promise竞速, 返回第一个完成的promise无论成功还是失败。
e. Promise.any([promise1, promise2, promise3]) 类似于Promise.race,但是它是返回的第一个已经成功的promise实例,如果全部失败则会报错
3. ES Module 是什么?为什么要引入ES Module? 与其他javascript模块化机制(CommonJS)有什么不同?
模块是能够独立实现某一功能的代码集合。ES6 新出了一种模块机制:ES Module,它主要分为两个命令:导入import 和 导出 export。import的引入需要置于作用域首部,它可以单独引入整个模块,或是模块中的部分变量或函数。 export 也可以单独导出若干变量/函数或者是export default xxx 即导出xxx并将其作为整体的默认模块。
我们引入es module有若干优势:
- 首先,它进行了作用域的隔离,即通过模块作用域的束缚,避免了模块中的内部变量/函数对其父级作用域中的变量/函数的代码污染。
- 可以按需加载,即我们可以只用import具体某几个变量或函数,而不需要全量代码的应用。
- 可以复用代码,减少代码冗余,提高了代码的整洁度。
- es module在编译时即可确定其依赖,可以通过tree-shaking等手段优化代码。
CommonJS 是早期node.js的模块引入机制,node.js默认有一个module对象,通过属性exports将要导出的对象绑定到属性中。通过require导入exports对象。
ESModule与CommonJS的区别:
- ESModule 是导入模块对象的引用,而CommonJS导出的是模块对象的浅拷贝
- ESModule的模块作用域自动开启严格模式,而CommonJS并没有这样做。
- Esmodule是在编译时执行 但是CommonJS是运行时加载
- ESModule 能动态导入若干个值 但是CommonJS只能导入一个值 (module.exports 属性对象)
- ESModule 的import必须在作用域顶层引入(除非使用import函数引入)CommonJS可以在代码任意位置动态引入。
- ESModle浏览器是默认支持,但是CommonJS不支持,需要通过代码转义。
- ESmodule可以在编译时优化但CommonJS不行。