由于目前现在求职市场竞争激烈,许多初学者和经验丰富的开发人员都面临着求职问题。因此,最好的方法是不断学习并提高自己的技能。
为了拿到心仪的offer,我们需要认真准备面试,因此,今天我为大家准备了53道面试题,我以“一问一答”的形式将这些前端面试题分享出来,希望能够帮助到你。
虽然这些面试题主要针对初级开发人员,但其中也有包括一些中级开发人员的题。
那么,我们现在开始吧。
const bigInt = 1234567890123456789012345678901234567890n;
运算符“==”检查抽象相等,而“===”检查严格相等。换句话说,“==”运算符在比较之前执行必要的类型转换,而“===”则不执行类型转换。因此,如果两个值不是同一类型,则使用“===”运算符时将返回 false。
声明变量有 4 种方法:声明变量有 4 种方法:
foo =123;
var foo = 123;
let a = 123;
conest a = 123;
使用“var”关键字声明变量与第一种方法类似。 以这种方式声明的变量具有全局或函数作用域,但缺少块作用域,这是一个缺点。
“let”和“const”是声明变量的更好方法。 它们具有块作用域,这意味着在函数内部声明的变量在该函数外部将不可见。
“const”变量是不可变的,但如果它是一个对象,你可以改变它的属性,如果它是一个数组,你可以修改和添加元素。
两个选项都代表空值。 如果我们初始化一个变量但不给它赋值,它就会被分配一个特殊的标记—undefined。Null 是手动分配的。Null 是一个特殊值,表示“无”、“空”或“未知值”。 如果我们需要清除变量的值,我们设置 foo = null。
闭包是一个函数以及它可以访问的所有外部变量。 例如,有一个具有嵌套函数的函数,该函数将关闭并保留其父级的变量。
模板文字用反引号 (") 括起来,并允许多行字符串。 它们还允许在其中嵌入表达式。
Map是一个集合,是一种按照键值对原理进行操作的数据结构,类似于Object。 然而,Map 和 Object 之间的主要区别在于 Map 允许使用任何类型的键。Set 是一种没有键的集合类型,是一个数组,其中每个值只能出现一次。 集合在其内部存储唯一的值。
第一种方法是使用 hasOwnProperty 函数,该函数适用于每个对象。第二种方法是使用 in 运算符。 但是,在使用 in 运算符时必须小心,因为它会检查链中的所有原型。
第一种方式是静态的,使用点表示法:obj.a。
第二种方式是动态的,使用方括号:obj[‘a’]。
使用构造函数:
使用对象文字表示法:
使用一个类:
使用创建函数:
Promise 是一个设计用于处理异步代码的对象。它维持自己的状态。最初,Promise 处于待处理状态,如果异步代码执行成功,则 Promise 转换为已完成状态;如果发生错误,则转换为拒绝状态。Promise 接受两个回调函数:
使用模式如下:
async/await 是一种处理 Promise 的特殊语法。
使用异步语法声明的函数始终返回 Promise。
关键字await使JavaScript解释器等待,直到await右侧的Promise完成后才继续执行。然后,它将返回结果,并且代码将继续执行。wait 不能在常规函数中使用。
要检查对象是否是数组,可以使用 Array.isArray() 方法。它接受一个对象作为输入,如果该对象是数组则返回 true,如果不是数组则返回 false。
扩展运算符 (…) 用于解包数组或对象。
它允许你扩展可迭代的元素,例如,数组和字符串。
它用在调用的预期参数数量为零或更多的函数中。
它用在数组文字或表达式中。
它用在对象文字中,其中键值对的数量应为零或更多。
如果对象不包含嵌套对象,例如:
在这种情况下,你可以使用展开运算符或 Object.assign() 方法:
如果对象包含嵌套对象:
在这种情况下,你需要执行深复制。
解决方法虽然较慢,但是:
该方法适用于没有原型和函数的对象。
或者,你可以使用 lodash 库的 deepClone() 函数。
使用bind()方法,该方法返回一个带有绑定上下文的新函数。
使用 call() 和 apply() 方法。主要区别在于 call() 接受参数序列,而 apply() 接受参数数组作为第二个参数。
三元运算符是 if-else 语句的简写符号。运算符由问号和冒号表示。它被称为三元,因为它是唯一接受三个参数的运算符。
健康)状况 ?表达式_1:表达式_2
解构是一种允许我们将数组和对象解包为多个变量的语法。
DOM 代表文档对象模型。它是将 HTML 文档表示为标签树。
例子
DOM 树中的每个节点都是一个对象。
HTML 文档的基本元素是标签。
根据文档对象模型 (DOM),每个 HTML 标签都是一个对象。嵌套标签是其父元素的“子元素”。标签内的文本也是一个对象。所有这些对象都可以使用 JavaScript 访问,我们可以使用它们来操作页面。
事件循环——一种管理代码执行的机制。它以正确的顺序处理事件处理和任务执行。
事件循环的主要思想是JavaScript在单线程环境中运行但可以处理异步操作。当异步操作(例如服务器请求)完成时,它将相应的事件放入事件队列中。
事件循环以循环方式工作,按照事件到达的顺序处理这些事件。
它从队列中获取一个事件并将其传递以供执行。如果事件包含回调或处理程序,则会调用它,并执行与该事件关联的代码。
事件循环还处理其他任务,例如计时器和微任务(Promise)。它管理所有这些任务的执行顺序,以确保一致性并防止阻塞代码执行的主线程。
简而言之,JavaScript 中的事件循环通过处理队列中的事件并按正确的顺序执行相应的代码来管理异步操作。这使得 JavaScript 在处理异步操作时能够响应并有效地利用其资源。
JavaScript 中的每个对象都有一个属性——原型。可以将方法和属性添加到原型中。可以根据原型创建其他对象。创建的对象自动继承其原型的方法和属性。如果对象中不存在某个属性,则将在原型中执行其搜索。
可选链接运算符 ?。如果 ? 后面的部分停止计算并返回undefined。undefined或为null。
让我们考虑一个用户对象。大多数用户都有一个地址 user.address 和一个街道 user.address.street,但有些用户没有提供地址。在这种情况下,可选链接运算符可以帮助我们在尝试访问未在地址中指定街道的用户街道时避免错误。
Shadow DOM 是一组 Web 标准,允许封装网页上元素的结构和样式。它代表 DOM 的一个特殊片段,位于元素内部并与页面的其余部分分开。Shadow DOM 用于创建具有独立且风格化内容的组件和小部件,这些内容与页面的整体结构不冲突。
递归是一种解决问题的方法,其中函数通过在自己的函数体内重用自身来解决问题。简单来说,就是函数调用自身的时候。
递归函数包括:
基本情况是必要条件;否则,会因函数调用无限循环而导致堆栈溢出。
函数声明是声明函数的传统方式。
函数表达式:
通过函数声明,可以创建函数并将其分配给变量,就像任何其他值一样。本质上,函数如何定义并不重要,因为它是存储在变量“foo”中的值。
然而,函数声明是在执行代码块之前处理的,这意味着它们在整个代码块中都是可见的。另一方面,函数表达式仅在执行流到达时才会创建。
构造函数是用于创建对象的常规函数。但是,使用它们有两个规则:
当使用 new 运算符创建构造函数时,会发生以下情况:
你可以使用 Object.keys() 获取键列表,使用 Object.values() 获取值列表。
最常见的:
类继承是使用“extends”关键字后跟父类的名称来完成的。
在 JavaScript 中,微任务和宏任务是指需要在事件循环中执行的任务类型。
微任务是在浏览器重新绘制页面之前需要在当前事件循环内执行的任务。它们通常使用 Promise.then()、process.nextTick()(在 Node.js 中)或 MutationObserver 等方法添加到执行队列中。
微任务的示例包括执行 Promise 处理程序和 DOM 突变。
另一方面,宏任务是在当前事件循环完成之后、在屏幕上呈现更改之前需要执行的任务。
这包括使用 setTimeout、setInterval、requestAnimationFrame 添加到事件队列的任务,以及处理输入事件和网络请求。
宏任务在当前事件循环中的所有微任务处理完毕后执行。
微任务和宏任务之间的区别很重要,因为它决定了执行顺序并允许管理 JavaScript 中不同任务的优先级。
微任务具有更高的优先级,并且在宏任务之前执行,这样可以更快地更新界面并防止阻塞主 JavaScript 执行线程。
生成器根据需要一个一个地生成一系列值。生成器可以很好地与对象配合使用,并且可以轻松创建数据流。
要声明生成器,需要使用一种特殊的语法——生成器函数。
next() 是生成器的主要方法。调用时,next() 开始执行代码,直到最近的yield 语句。该值可能不存在,在这种情况下它表示为未定义。当达到yield时,函数执行暂停,并将相应的值返回给外部代码。
在浏览器中存储数据有多种方法:
SessionStorage 和 localStorage 允许在浏览器中以键值格式存储对象。
主要区别是:
正则表达式是由特殊规则和模式定义的字符串。它们是一个强大的工具,可以检测和处理字符串中的复杂结构。
WeakMap 和 Map 之间的第一个区别是 WeakMap 中的键必须是对象,而不是原始值。
第二个区别在于数据结构的内存存储。JavaScript 引擎将值保存在内存中,只要它们是可访问的,这意味着它们可以被使用。
通常,对象属性、数组元素或其他数据结构被认为是可访问的,并且只要数据结构存在,它们就会保留在内存中,即使没有其他对它们的引用。
对于 WeakMap 和 WeakSet 来说,它的工作方式不同。一旦对象变得不可访问,它将从数据结构中删除。
根据对内存区域的引用来比较对象。对于 JavaScript,test1 和 test2 对象是不同的,即使它们具有相同的字段。仅当对象是同一个对象时,它们才相等。
JavaScript 允许使用原始数据类型(字符串、数字等),就像它们是对象一样。原始数据类型有方法。
为了使此功能可用,每个基本数据类型都有自己的包装对象:字符串、数字、布尔值和符号。由于这些包装对象,原始数据类型具有不同的方法集,例如 toLowerCase() 或 toUpperCase()。
你可以使用instanceof运算符检查对象是从哪个类创建的,同时考虑继承。
纯函数需要满足以下两个条件的函数:
高阶函数是接受另一个函数作为参数或返回一个函数作为结果的函数。
如果我们想使用回调函数从服务器异步获取一些数据,则会导致以下结果:
这称为回调地狱,因为每个回调都嵌套在另一个回调中,并且每个内部回调都依赖于父函数。
使用Promises,我们可以重写上面的代码:
有了Promises,执行顺序就清晰了,让代码更具可读性。
为了实现它,我们可以使用闭包和 apply() 方法将函数绑定到上下文。
你可以使用 sort() 方法和 Math.random() 来实现此目的。
我们应该迭代每个嵌套数组,获取每个嵌套数组的最大值并将其删除。
让我们创建一个函数reverseLinkedList,它将链表作为输入并返回该列表的反转版本。
方法:
总之,该函数通过从头到尾迭代每个节点,为每个值创建一个新的列表节点并相应地更新指针来反转链表。
让我们创建一个函数 sortList,它将链表作为输入并返回该列表的排序版本。
方法:
Observables 和 Promises 都用于处理 JavaScript 中的异步操作。
一个关键的区别是 Observables 可以随着时间的推移发出多个值。
它们适合处理数据流,例如用户交互、事件或来自 API 的随时间变化的数据。另一方面,承诺只能用单个值解析一次。
它们适合处理要么成功要么失败的单个异步操作。
总之,Promises 最适合处理具有单个结果的一次性异步操作,而 Observables 在处理持续的数据流、事件和复杂的数据处理管道时更强大。
它们之间的选择取决于特定的用例以及你正在处理的异步操作的性质。
在准备这些面试题以及研究所涵盖的主题并查看相关资源的时候,相当于又把一些知识做了复习,对于之前没有记住的内容,通过对这些内容的掌握,可以提升你的面试成功通过机率。