我从事 JavaScript 面试已经有一段时间了,我遇到过很多人,尽管知道自己要参加 JavaScript 面试,但他们要么不熟悉 JavaScript,要么没有做好充分的准备。
尽管从事过基于 JavaScript 的项目,但其中一些人甚至无法回答最基本的 JavaScript 问题。因此,本文的目的是分享在JavaScript 面试中会遇到哪些问题以及如何回答这些问题。
这些不是我在采访中提出的确切问题,但它们确实涵盖了概念。让我们从一些基本问题开始,然后逐步讨论更高级的概念。
1. var、let 和 const 之间的区别?
尽管这是 JS 的基本问题之一,但令人惊讶的是,许多人都无法区分 let 和 var 之间的区别。
答案很简单:var 是函数作用域,可以重新声明和更新,而 let 是块作用域,不能重新声明,但可以更新。同时,const 是块作用域的,不能重新声明或更新。
var a = 10 // Can re-declared and update
let b = 20 // Can't re-declared but can update
const c = 30 // Can't re-declared or update
当然,var 如今已经不太常用了,但是仍然有代码使用它,我们可能会遇到。未能回答此问题还表明你不熟悉 JavaScript 中块作用域和函数作用域之间的区别。
2. == 和 === 运算符的区别?
这也是一个很常见的JS问题。== 运算符仅检查值是否匹配,而忽略数据类型。如果要比较的两个变量的类型不同,它会在后台执行类型强制。另一方面, === 运算符确保值和类型相等。
2 == "2" //true
2 === "2" //false
null == undefined //true
null === undefined //false
3. 如何在不改变原始数组的情况下对数组进行排序?
在 2023 年之前,要对数组进行排序而不改变它,你需要对其进行浅表复制,因为 JS 排序函数会改变原始数组。
因此,最简单的方法是使用扩展运算符,然后对数组进行排序,如下例所示。
const numbers = [3, 1, 4, 1, 5];
const sorted = [...numbers].sort();
然而,在 2023 年,JS 引入了 toSorted() 函数,它允许您在不改变原始数组的情况下对数组进行排序。这个问题测试候选人的知识以及是否是最新的。
const numbers = [3, 1, 4, 1, 5];
const sorted = numbers.toSorted();
4. 解释回调函数并提供用例?
简而言之,回调是一个函数,它作为参数传递给另一个函数,然后在后者完成其任务后执行。它支持异步操作,允许 JavaScript 处理耗时的任务,而不会延迟其他操作的执行。
const fetchData = (callbackFunc) => {
setTimeout(() => {
const data = "Sample data";
callbackFunc(data);
}, 2000);
}
const processFetchedData = (data) => {
console.log("Processing data:", data);
}
fetchData(processFetchedData);
回调用于处理用户事件,例如按钮单击和输入更改。当用户事件发生时,可以调用回调函数来执行一段代码。回调对于处理异步数据请求也非常有用。当从服务器获取数据时,回调函数处理接收到的数据。
5. JavaScript 中的闭包是什么?
闭包是一个可以访问其自身作用域、外部函数作用域和全局作用域的函数。闭包还与 JavaScript 中的词法作用域相关。词法作用域描述了变量和函数在运行时如何确定作用域。为了解释什么是闭包,最好用一个简单的例子。
let makeSizer = (size) =>
() => {
console.log(`fontSize = ${size}px`);
};
const size10 = makeSizer(10);
const size12 = makeSizer(12);
const size14 = makeSizer(14);
size10(); // fontSize = 10px
size12(); // fontSize = 12px
size14(); // fontSize = 14px
在前面的示例中,size10、size12 和 size14 函数是闭包。闭包在事件驱动编程中很有用。例如,你可以编写一个生成事件处理程序的函数,每个处理程序都可以访问特定数据,而不会污染全局范围。
6.什么时候需要使用Async代码?
当我们需要避免 JavaScript 中阻塞主线程时,我们必须使用异步代码。主线程管理调用堆栈,其中包含当前的函数调用序列。如果主线程被阻塞,函数在调用之前将必须等待。
因此,异步代码对于高效处理耗时操作至关重要。JavaScript 提供了回调、promise 和 async/await 等技术来更好地管理异步操作。
7. JavaScript 中的 Promise 是什么?
Promise 是代表异步操作最终成功或失败的对象。它们是与 ES6 JavaScript 添加一起引入的。当 Promise 被解决或拒绝时,您可以使用 Promise 实例的 then 和 catch 方法来调用函数。以下是 Promise 的简单 JavaScript 代码示例。
const fetchData = () => {
return new Promise((resolve, reject) => {
// Simulate an asynchronous operation
setTimeout(() => {
// Resolve the promise with some data
resolve("Data fetched successfully!");
}, 1000);
});
};
// Use the Promise
fetchData()
.then(data => {
console.log(data);
})
.catch(error => {
console.error("An error occurred:", error);
});
8. Promise 和 Observables 之间的区别?
这是一个比前面的问题更高级的问题。Observables 是 Promise 的更强大版本,但它们不是 Javascript 原生的。它们用于处理产生多个值的操作,例如,用户输入事件或实时数据流。
与 Promises 不同,Observables 允许你通过调用 unsubscribe() 方法来取消正在进行的操作。Observables 也比 Promises 更适合复杂的数据操作,因为它们支持更广泛的运算符(例如 map、filter、switchMap 等)。
然而,Observables 要求应用程序包含 RxJS 依赖开销。此外,Observables 比 Promise 更难使用。因此,如果需要一种简单的方法来处理异步操作的单个结果,Promise 更适合。
9. Promise 和 Async Await 之间的区别?
Async/Await 具有比 Promises 更同步的语法,使阅读和理解程序的流程变得更容易。与 Promise 链相比,Async/Await 的样板代码更少。链接是连续执行两个或多个异步操作,每个后续操作在前一个操作成功完成后开始。
const fetchData = async () => {
// Simulate an API call that returns a promise
let promise = new Promise((resolve, reject) => {
setTimeout(() => resolve("Data fetched!"), 1000);
});
let result = await promise;
console.log(result);
}
fetchData().catch(error => {
console.error("An error occurred:", error);
});
上面是 async/await 在 JavaScript 中如何工作的示例。wait 关键字指示 JavaScript 等待,直到 Promise 对象被解析或拒绝。
Async/Await 基于 Promises,并作为对开发人员更友好的语法在 Promises 之后添加到 JavaScript 中。
但是,在必须并发处理多个异步操作的场景中,应该使用 Promises 而不是 async/await。Promise.all() 方法在这种情况下会很有用。
10. 解释一下去抖动和节流?
去抖动意味着延迟函数的执行,直到自上次触发事件以来经过一定时间。另一方面,节流限制了调用函数的频率。它确保函数以指定的时间间隔执行,并且该时间间隔内的任何其他调用都将被忽略。
用例
去抖动最常见的应用之一是搜索建议,您希望等待用户完成输入后再获取建议。这避免了每次击键时都调用 API 并提高了性能。
限制对于处理滚动和调整大小事件非常有用,可以在这些事件中限制函数调用的频率,以便应用程序不会因重新渲染而过载。
11. 什么是 JavaScript 中的事件冒泡以及如何阻止它?
当你单击嵌套在其他几个元素中的按钮时,该按钮及其父元素都会触发单击事件,从而在 DOM 树中向上移动。这称为事件冒泡,DOM 中的大多数事件都会发生这种情况。如果组件具有嵌套的单击事件,这可能会成为问题。
要停止事件冒泡,可以使用事件对象的 stopPropagation() 方法。此方法可防止事件在 DOM 树上进一步传播。
onClick((event) => {
event.stopPropagation();
console.log("button clicked");
})
总结
以上这11道面试题中有基本和高级 JavaScript 问题与概念。作为面试官,我试图使这些信息尽可能简单,以便即使是初学者也能理解。
我希望今天分享的这期内容能够帮助你自信地面对下一次 JavaScript 面试。最后,感谢您的阅读,祝编程愉快。