52 个前端面试题 - JavaScript

2025-05-25

52 个前端面试题 - JavaScript

介绍

大量新手和经验丰富的开发人员都开始遇到并意识到求职的问题。事实上,市场竞争水平比两三年前高得多。

在当今现实中,最好的做法就是不断学习。
在本文中,我整理了 52 个面试中遇到的前端开发人员面试问题,采用“问答”的形式。这些问题主要面向初级水平,也部分面向中级水平。

PS:尽管大多数问题都是针对初级水平的,但通过准备这些问题并彻底研究主题,我能够获得中级前端开发人员的工作。

1. JavaScript 中有哪些数据类型?

  1. 数字 - 数字
  2. 字符串 - 字符串
  3. Boolean - 布尔类型,真或假
  4. 对象 - JavaScript 对象
  5. null - 表示“无”、“空”或“未知值”的特殊值。
  6. undefined - “值尚未赋值”。如果变量已声明但尚未赋值,则为该类型。
  7. 符号——一种唯一且不可变的数据类型,可用作对象属性的标识符。
  8. BigInt——用于创建大数字。

const bigInt = 1234567890123456789012345678901234567890n;

了解更多

2.“==”和“===”有什么区别?

运算符“==”检查抽象相等性,而“===”检查严格相等性。
换句话说,“==”运算符在比较之前会进行必要的类型转换,而“===”则不会进行类型转换。因此,如果两个值的类型不同,使用“===”运算符将返回 false。

了解更多

3. 声明变量有哪些方式?

声明变量有四种方法:

  foo = 123; 
  var foo = 123; 
  let a = 123; 
  const a = 123; 
Enter fullscreen mode Exit fullscreen mode

使用“var”关键字声明变量与第一种方法类似。这种方式声明的变量具有全局或函数作用域,缺乏块作用域,这是一个缺点。
“let”和“const”是声明变量的更佳方式。它们具有块作用域,这意味着在函数内部声明的变量(例如,在函数内部)在该函数外部不可见。“const”变量是不可变的,但如果它是一个对象,则可以更改其属性;如果它是一个数组,则可以修改和添加元素。

了解更多

4.null和undefined有什么区别?

这两个选项都表示空值。如果我们初始化一个变量但没有赋值,它会被赋予一个特殊标记——undefined。Null 是手动赋值的。Null

是一个特殊值,表示“无”、“空”或“未知值”。如果我们需要清除变量的值,可以设置 foo = null。

了解更多

5.箭头函数以及与常规函数的区别。

  1. 箭头函数不能使用参数对象。
  2. 它们有不同的语法。
  3. 箭头函数没有自己的 this 上下文。当引用 this 时,箭头函数会从其周围的作用域获取上下文。
  4. 箭头函数不能用作构造函数。换句话说,它们不能用 new 关键字调用。

了解更多

6. 什么是闭包以及为什么需要闭包?

闭包是指一个函数及其可以访问的所有外部变量。例如,一个函数包含一个嵌套函数,该嵌套函数会将其父函数的变量封闭并保留。

function parent() { 
    const a = 5; 
    return function child() { 
        console.log(5); // child closes over the variable 'a'; 
    } 
}
Enter fullscreen mode Exit fullscreen mode

了解更多

7.什么是模板文字?

模板字面量用反引号 ( ) 括起来,允许使用多行字符串。它们还允许在其中嵌入表达式。

const name = 'John';
const text = `User's name is ${name}`;

console.log(text) // User's name is John
Enter fullscreen mode Exit fullscreen mode

了解更多

8.什么是Set和Map?

Map 是一种集合,一种基于键值对原则的数据结构,类似于对象。然而,Map 和对象之间的主要区别在于,Map 允许使用任何类型的键。Set
是一种没有键的集合,它是一个数组,其中每个值只能出现一次。Set 本身存储唯一的值。

了解更多

9.如何检查对象中某个属性是否存在?

第一种方法是使用 hasOwnProperty 函数,该函数适用于每个对象。
第二种方法是使用 in 运算符。但是,使用 in 运算符时必须谨慎,因为它会检查链中的所有原型。

const obj = {
  year: 2023,
  name: "John"
}

console.log(obj.hasOwnProperty("year")) // true
console.log("year" in obj) // true
console.log("ye" in obj) // false
Enter fullscreen mode Exit fullscreen mode

了解更多

10.如何访问对象属性?

第一种方式是静态的,使用点符号:obj.a。
第二种方式是动态的,使用方括号:obj['a']。

const obj = {
  year: 2023,
  name: "John"
}

console.log(obj['year']) // 2023
console.log(obj.name) // John
Enter fullscreen mode Exit fullscreen mode

了解更多

11. 使用数组的主要方法有哪些?

  • forEach - 用于循环遍历数组的迭代方法,不返回任何值。它提供了一种比常规 for 循环更优雅的替代方案。
  • filter(callback, [args]) - 一种使用提供的函数过滤数组的方法。它会创建一个新数组,其中仅包含原始数组中符合回调函数 (item, i, arr) 返回 true 的元素。
  • map(callback, [args]) - 一种转换数组的方法。它会创建一个新数组,其中包含对数组中每个元素调用回调函数 (item, i, arr) 的结果。
  • reduce(callback, [initValue]) - 一种按顺序处理数组的每个元素同时保留中间结果的方法。

了解更多

12.创建对象有哪些方式?

使用构造函数:

function User(firstName, lastName) {
  this.firstName = firstName;
  this.lastName = lastName;
}
const user = new User('John', 'Johnson');
console.log(user); // { firstName: 'John', lastName: 'Johnson' }
Enter fullscreen mode Exit fullscreen mode

使用对象文字表示法:

const user = {
  firstName: 'John',
  lastName: 'Johnson'
};
Enter fullscreen mode Exit fullscreen mode

使用类:

class User {
  constructor(firstName, lastName) {
    this.firstName = firstName;
    this.lastName = lastName;
  }
}
const user = new User('John', 'Johnson');
console.log(user); // { firstName: 'John', lastName: 'Johnson' }
Enter fullscreen mode Exit fullscreen mode

使用创建函数:

const user = Object.create({
  firstName: 'John',
  lastName: 'Johnson'
});
Enter fullscreen mode Exit fullscreen mode

13.什么是承诺?

Promise 是一个专为异步代码而设计的对象。它维护自己的状态。最初,Promise 处于 pending 状态;如果异步代码执行成功,它会转换为 fulfilled 状态;如果发生错误,它会转换为 rejection 状态。Promise 接受两个回调函数:

  • onFulfilled,当 Promise 实现时触发。
  • onRejected,当Promise被拒绝时触发。

使用模式如下:

  1. 需要异步执行某些操作的代码会创建一个 Promise 并返回它。
  2. 外部代码在收到 Promise 后,将 onFulfilled 和 onRejected 回调函数传递给它。
  3. 当该过程完成时,异步代码将 Promise 转换为已实现或已拒绝状态,并自动调用相应的回调函数。

了解更多

14.什么是async/await以及如何使用它?

async/await 是用于处理 Promise 的特殊语法。
使用 async 语法声明的函数始终返回一个 Promise。
关键字 await 使 JavaScript 解释器等待 await 右侧的 Promise 满足条件后再继续执行。然后它将返回结果,代码将继续执行。await 不能在常规函数中使用。

了解更多

15.如何检查一个对象是否是一个数组?

要检查一个对象是否是数组,可以使用 Array.isArray() 方法。该方法以一个对象作为输入,如果该对象是数组,则返回 true;如果不是数组,则返回 false。

const obj1 = { person: 'John' }
const obj2 = new Array(2)
const obj3 = []

console.log(Array.isArray(obj1)) // false
console.log(Array.isArray(obj2)) // true
console.log(Array.isArray(obj3)) // true
Enter fullscreen mode Exit fullscreen mode

16. 展开运算符的用途是什么?

展开运算符 (...) 用于解包数组或对象。
它允许你扩展可迭代元素,例如数组和字符串。

  • 它用于调用预期参数数量为零或更多的函数中。
  • 它用于数组文字或表达式。
  • 它用于对象文字中,其中键值对的数量应该为零或更多。
const date = [2000, 3, 7] 
const newArray = [...date] // [2000, 3, 7]
Enter fullscreen mode Exit fullscreen mode

了解更多

17.复制对象时如何避免引用依赖?

如果对象不包含嵌套对象,例如:

const obj = {
 firstName: 'John',
 lastName: 'Johnson'
}
Enter fullscreen mode Exit fullscreen mode

在这种情况下,您可以使用扩展运算符Object.assign()方法:

const copy = {...obj}
// or
const copy = Object.assign({}, obj)
Enter fullscreen mode Exit fullscreen mode

如果对象包含嵌套对象:

const obj = {
    data: {
        id: 1
    }
}
Enter fullscreen mode Exit fullscreen mode

在这种情况下,您需要执行深层复制。
一种解决方法(虽然速度较慢)是:

const copy = JSON.parse(JSON.stringify(obj))
Enter fullscreen mode Exit fullscreen mode

此方法适用于没有原型和函数的对象。
或者,你可以使用 lodash 库的deepClone()函数。

18.如何改变函数的上下文?

  1. 使用bind()方法,该方法返回一个具有绑定上下文的新函数。
function foo() {
    return this
}
const obj = { name: 'John' }
const newFoo = foo.bind(obj)
console.log(newFoo()) // { name: 'John' }
Enter fullscreen mode Exit fullscreen mode
  1. 使用call()apply()方法。主要区别在于call()接受一个参数序列,而apply()接受一个参数数组作为第二个参数。
function foo() {
    return this
}

const obj = { name: 'John' }
foo.call(obj, 'arg1', 'arg2') // { name: 'John' }
foo.apply(obj, ['arg1', 'arg2']) // { name: 'John' }
Enter fullscreen mode Exit fullscreen mode

19.什么是三元运算符?

三元运算符是 if-else 语句的简写形式。该运算符由问号和冒号表示。之所以称为三元运算符,是因为它是唯一一个接受三个参数的运算符。

条件?表达式_1:表达式_2

num >= 10 ? 'more than 10' : 'less than 10'
// is equal to
if (num >= 10) {
    return 'more than or equal to 10'
}
return 'less than 10'
Enter fullscreen mode Exit fullscreen mode

了解更多

20.什么是解构?

解构是一种允许我们将数组和对象解包为多个变量的语法。

const arr = ['John', 'Johnson']
const [firstName, lastName] = arr
console.log(firstName, lastName) // John Johnson

OR

const obj = {
    firstName: 'John',
    lastName: 'Johnson'
}
const { firstName, lastName } = obj;
console.log(firstName, lastName) // John Johnson
Enter fullscreen mode Exit fullscreen mode

了解更多

21.什么是 DOM?

DOM 代表文档对象模型。它将 HTML 文档表示为一个标签树。
示例:
DOM 树中的每个节点都是一个对象。

JS DOM

HTML 文档的基本元素是标签。
根据文档对象模型 (DOM),每个 HTML 标签都是一个对象。嵌套标签是其父元素的“子元素”。标签内的文本也是一个对象。所有这些对象都可以使用 JavaScript 访问,我们可以使用它们来操作页面。

了解更多

22.什么是事件循环?

事件循环——一种管理代码执行的机制。它以正确的顺序处理事件和任务执行。事件循环的核心思想是 JavaScript 在单线程环境中运行,但可以处理异步操作。当异步操作(例如服务器请求)完成时,它会将相应的事件放入事件队列中。事件循环以循环方式工作,按照事件到达的顺序处理它们。它从队列中获取事件并将其传递给执行。如果事件包含回调或处理程序,则会调用该事件,并执行与该事件关联的代码。事件循环还处理其他任务,例如计时器和微任务(Promise)。它管理所有这些任务的执行顺序,以确保一致性并防止代码执行的主线程被阻塞。

简而言之,JavaScript 中的事件循环通过处理队列中的事件并以正确的顺序执行相应的代码来管理异步操作。这使得 JavaScript 在处理异步操作时能够快速响应并有效利用其资源。

事件循环

我强烈建议观看所提供链接中的视频,因为该主题很重要,值得单独写一篇文章。

了解更多

23.什么是原型继承?

JavaScript 中的每个对象都有一个属性——原型。方法和属性可以添加到原型中。其他对象可以基于原型创建。创建的对象会自动继承其原型的方法和属性。如果对象中缺少某个属性,则会在原型中查找。
了解更多

24.什么是可选链接运算符?

如果 ?. 之后的部分未定义或为空,则可选链接运算符 ?. 将停止评估并返回未定义。

让我们考虑一个用户对象。大多数用户都有一个地址 user.address 和一个街道 user.address.street,但有些用户没有提供地址。在这种情况下,可选链式运算符可以帮助我们避免在尝试访问未在地址中指定街道的用户时出现错误。

const user = {};

console.log(user.address.street) // Error!
console.log(user?.address?.street) // undefined. No Error
Enter fullscreen mode Exit fullscreen mode

了解更多

25.什么是 Shadow DOM?

Shadow DOM 是一套网络标准,用于封装网页元素的结构和样式。它代表 DOM 中位于元素内部、与页面其余部分分离的特殊部分。Shadow DOM 用于创建具有独立且风格化内容的组件和小部件,这些内容不会与页面的整体结构冲突。
了解更多

26.什么是递归?如何使用它?

递归是一种解决问题的方法,其中函数通过在其函数体内重用自身来解决问题。简单来说,就是函数调用自身。

递归函数包括:

  1. 终止条件或基准情况
  2. 递归步骤——将问题简化为更简单形式的一种方式。
function factorial(x) {
   if (x === 0) {
      return 1;
   }
   return x * factorial(x - 1);
}
Enter fullscreen mode Exit fullscreen mode

基本情况是必要条件;否则,函数调用会无限循环,导致堆栈溢出。
了解更多

27.函数表达式和函数声明有什么区别?

函数声明是声明函数的传统方式。

function foo() {
    console.log('Hello World');
}
Enter fullscreen mode Exit fullscreen mode

函数表达式:

let foo = function() {
    console.log('Hello World');
}
Enter fullscreen mode Exit fullscreen mode

使用函数声明时,函数会被创建并赋值给一个变量,就像任何其他值一样。本质上,函数的定义方式并不重要,因为它是一个存储在变量“foo”中的值。然而,函数声明会在代码块执行之前被处理,这意味着它们在整个代码块中都是可见的。而函数表达式则仅在执行流到达时才会创建。

了解更多

28.什么是构造函数?

构造函数是用于创建对象的常规函数​​。但是,使用它们有两个规则:

  1. 构造函数的名称应以大写字母开头。
  2. 应该使用 new 运算符来调用构造函数。
function User(firstName, lastName) {
    this.firstName = firstName
    this.lastName = lastName
    this.role = 'user'
}

const user = new User('John', 'Johnson')
console.log(user.firstName) // John
Enter fullscreen mode Exit fullscreen mode

当使用 new 运算符创建构造函数时,会发生以下情况:

  1. 创建一个新的空对象并将其分配给此。
  2. 构造函数内部的代码被执行。通常,这些代码会修改 this 对象并添加新的属性。
  3. 返回此值。

了解更多

29. 如何从对象中获取键列表和值列表?

您可以使用 Object.keys() 获取键列表,使用 Object.values() 获取值列表。

const user = {
    firstName: 'John',
    lastName: 'Johnson'
}

const keys = Object.keys(user)
const values = Object.values(user)

console.log(keys) // ['firstName', 'lastName']
console.log(values) // ['John', 'Johnson'] 
Enter fullscreen mode Exit fullscreen mode

了解更多

30.提供ES6新功能的示例。

最常见的有:

  • let 和 const。引入新关键字 let 和 const,用于声明具有块作用域的变量。
  • 箭头函数。箭头函数的概念使得函数定义更加简洁、清晰。
function add(a, b) { return a + b } // Regular function
const add = (a, b) => a + b // Arrow function
Enter fullscreen mode Exit fullscreen mode
  • 默认参数。您可以为函数参数定义默认值。
function greet(name = 'Anonymous') { console.log(Hello, ${name}!) }
greet(); // "Hello, Anonymous!"
greet('John') // "Hello, John!"
Enter fullscreen mode Exit fullscreen mode
  • 展开运算符 (...)。展开运算符允许解包数组或对象元素作为函数参数,或者创建新的数组/对象。
const numbers = [1, 2, 3];
console.log(...numbers) // 1 2 3
const array1 = [1, 2, 3]; 
const array2 = [...array1, 4, 5] // [1, 2, 3, 4, 5]
Enter fullscreen mode Exit fullscreen mode
  • 解构。解构允许从数组或对象中提取值并将其分配给变量。
const person = { name: 'John', age: 30, city: 'London' }
const { name, age } = person;
console.log(name, age) // "John 30"
const numbers = [1, 2, 3]
const [first, second] = numbers;
console.log(first, second); // 1 2
Enter fullscreen mode Exit fullscreen mode

了解更多

31. ES6中如何进行类继承?

类继承是使用“extends”关键字加上父类的名称来完成的。

class User {
    firstName = 'John'
    lastName = 'Johnson'
}

class Customer extends User {
    cart
}
Enter fullscreen mode Exit fullscreen mode

了解更多
甚至更多

32. JavaScript 中的微任务和宏任务是什么?

在 JavaScript 中,微任务和宏任务是指需要在事件循环中执行的任务类型。微任务是指需要在浏览器重绘页面之前在当前事件循环中执行的任务。它们通常使用 Promise.then()、process.nextTick()(在 Node.js 中)或 MutationObserver 等方法添加到执行队列中。微任务的示例包括执行 Promise 处理程序和 DOM 修改。另一方面,宏任务是指需要在当前事件循环结束后、更改呈现到屏幕上之前执行的任务。这包括使用 setTimeout、setInterval、requestAnimationFrame 添加到事件队列的任务,以及处理输入事件和网络请求的任务。宏任务在当前事件循环中的所有微任务处理完毕后执行。微任务和宏任务之间的区别非常重要,因为它决定了执行顺序,并允许管理 JavaScript 中不同任务的优先级。微任务具有更高的优先级,并且在宏任务之前执行,从而可以更快地更新界面,并避免阻塞主 JavaScript 执行线程。
了解更多

33.什么是生成器?

生成器根据需要逐个生成一系列值。生成器可以很好地与对象配合使用,并轻松创建数据流。

要声明生成器,需要使用一种特殊的语法——生成器函数。

function* generateSomething() {
    yield 10;
    yield 20;
    yield 30;
    return 40;
}
Enter fullscreen mode Exit fullscreen mode

next() 是生成器的主要方法。调用时,next() 会开始执行代码,直到遇到最近的 yield 语句。值可能不存在,在这种情况下会表示为 undefined。当遇到 yield 语句时,函数执行会暂停,并将相应的值返回给外部代码。

let generator = generateSomething();
let first = generator.next();
Enter fullscreen mode Exit fullscreen mode

了解更多

34.浏览器中存储数据的方法有哪些?

浏览器中有多种存储数据的方法:

  • LocalStorage 和 SessionStorage - 在浏览器中存储键值对。存储在其中的数据在页面刷新后仍会保留。这两种存储方式都只能使用字符串作为键和值,因此需要使用 JSON.stringify() 进行对象转换。
  • Cookie - 存储在浏览器中的一小串数据。Cookie 通常由 Web 服务器使用 Set-Cookie 标头设置。浏览器会使用 Cookie 标头自动将它们添加到几乎每个对同一域名的请求中。一个 Cookie 最多可容纳 4KB 的数据。根据浏览器的不同,每个站点允许设置 20 个以上的 Cookie。
  • IndexedDB - 一个内置数据库,比 localStorage 更强大。它是一个键值存储,支持多种类型的键,值几乎可以是任何类型。IndexedDB 支持事务以确保可靠性,支持键范围查询和索引,并且允许存储比 localStorage 更多的数据。IndexedDB 专为离线应用而设计,可以与 Service Workers 和其他技术结合使用。

了解更多
了解更多
了解更多

35.sessionStorage和localStorage有什么区别?

SessionStorage 和 localStorage 允许在浏览器中以键值格式存储对象。
主要区别在于:

  • localStorage 最多可以存储 10 MB 的数据,而 sessionStorage 最多可以存储 5 MB 的数据。
  • localStorage 中的数据不会被删除,而 sessionStorage 中的数据在浏览器标签页关闭时会被删除。
  • localStorage 中的数据可从任何窗口访问,而 sessionStorage 中的数据只能从同一浏览器窗口访问。了解更多

36.什么是正则表达式?

正则表达式是由特殊规则和模式定义的字符串。它们是一个强大的工具,可以检测和处理字符串中的复杂结构。

let str = "We will, we will rock you"

console.log(str.match(/we/gi)) // ['We', 'we']
Enter fullscreen mode Exit fullscreen mode

了解更多

37.什么是 WeakSet 和 WeakMap,它们与 Map 和 Set 有何不同?

WeakMap 和 Map 的第一个区别在于 WeakMap 中的键必须是对象,而不是原始值。
第二个区别在于数据结构的内存存储。JavaScript 引擎会将值保存在内存中,只要它们可访问即可,这意味着它们可以使用。
通常,对象属性、数组元素或其他数据结构被认为是可访问的,只要数据结构存在,它们就会保存在内存中,即使没有其他对它们的引用。
对于 WeakMap 和 WeakSet,其工作方式有所不同。一旦对象变得不可访问,它就会从数据结构中移除。

了解更多

38. 为什么两个具有相同字段的对象在比较时返回 false?

对象比较基于对内存区域的引用。对于 JavaScript 来说,即使 test1 和 test2 对象具有相同的字段,它们也是不同的。只有当它们是同一个对象时,它们才相等。

const test1 = { value: 3 }
const test2 = { value: 3 }
console.log(test1 == test2) // false
Enter fullscreen mode Exit fullscreen mode

39.为什么我们可以调用原始类型的方法?

JavaScript 允许将原始数据类型(例如字符串、数字等)当作对象来处理。原始数据类型拥有方法。
为了实现此功能,每种原始数据类型都有各自的包装器对象:String、Number、Boolean 和 Symbol。得益于这些包装器对象,原始数据类型拥有不同的方法集,例如 toLowerCase() 或 toUpperCase()。

了解更多

40.如何检查对象是由哪个类创建的?

您可以使用instanceof运算符检查对象是从哪个类创建的,同时考虑到继承。

class Person {}
const person = new Person()
console.log(person instanceof Person) // true
Enter fullscreen mode Exit fullscreen mode

了解更多

41. 编写代码,每 10 秒记录一次在网站上花费的时间(以秒为单位)。

let time = 0
setInterval(() => {
    time += 10
    console.log(time)
}, 10000)
Enter fullscreen mode Exit fullscreen mode

了解更多

42.什么是纯函数?

纯函数是满足两个条件的函数:

  1. 每次使用同一组参数调用该函数时,它都会返回相同的结果。
  2. 它没有副作用,这意味着它不会修改函数外部的变量。
function calculate(num) {
    return calculate * 0.05;
}
console.log(calculate(15)) 
//calculate() function will always return the same result if we pass the same parameter
Enter fullscreen mode Exit fullscreen mode

43. 什么是高阶函数?

高阶函数是将另一个函数作为参数或返回一个函数作为结果的函数。

const nums1 = [1, 2, 3]
const nums2 = nums1.map(function(num) {
    return num * 2;
})
console.log(nums2) // [2, 4, 6]
Enter fullscreen mode Exit fullscreen mode

44.如果我们可以使用回调来处理异步代码,为什么还需要 Promises 呢?

如果我们想使用回调函数异步地从服务器获取一些数据,则会导致以下结果:

func((x) => {
  anotherFunc(x, (y) => {
    andAnotherFunc(i, (j) => {
      // some code
    })
  })
})
Enter fullscreen mode Exit fullscreen mode

这被称为回调地狱,因为每个回调都嵌套在另一个回调中,并且每个内部回调都依赖于父函数。

使用 Promises,我们可以重写上面的代码:

func()
.then((x) => {
  return anotherFunc(x)
})
.then((y) => {
  return andAnotherFunc(y)
})
.then((i) => {
  return i
})
Enter fullscreen mode Exit fullscreen mode

Promises 的执行顺序清晰,代码可读性更强。
了解更多

45.编写自己的bind方法实现。

为了实现它,我们可以使用闭包和apply()方法将函数绑定到上下文。

function bind(context, func) {
  return function(...args) {
    func.apply(context, args)
  }
}
Enter fullscreen mode Exit fullscreen mode

46. 编写一个计算器函数,包含加、减、乘、除和获取方法。该函数必须通过可选链式调用。

function calculator() {
  let result = 0;

  function plus(val) {
    result += val;
    return this;
  }

  function minus(val) {
    result -= val;
    return this;
  }

  function divide(val) {
    result /= val;
    return this;
  }

  function multiply(val) {
    result *= val;
    return this;
  }

  function get() {
    console.log(result);
    return this;
  }

  return { plus, minus, divide, multiply, get };
}

let calc = calculator();
calc.plus(2).minus(1).plus(19).divide(2).multiply(3).get(); // 30
Enter fullscreen mode Exit fullscreen mode

47. 编写一个 randomSort 函数,接受一个数字数组并按随机顺序对该数组进行排序。

您可以为此使用 sort() 方法和 Math.random()。

function randomSort(array) {
  return array.sort(() => {
    return 0.5 - Math.random();
  });
}
const arr = [2, 1, 3, -2, 9]
console.log(randomSort(arr)) // [-2, 2, 1, 3, 9]
console.log(randomSort(arr)) // [2, 1, -2, 9, 3]
console.log(randomSort(arr)) // [-2, 1, 9, 2, 3]
console.log(randomSort(arr)) // [1, -2, 2, 3, 9]
Enter fullscreen mode Exit fullscreen mode

48. 编写一个 deleteGreatestValue 函数,该函数接受一个二维数字数组,并从每个嵌套数组中删除最大的数字。

我们应该遍历每个嵌套数组,获取每个嵌套数组的最大值并将其删除。

function deleteGreatestValue(array) {
  for (let i = 0; i < array.length; i++) {
    const max = Math.max(...array[i]);
    const maxIndex = array[i].indexOf(max);
    array[i].splice(maxIndex, 1);
  }
  return array;
}

const arr = [[1, 4, 4], [2, 6, 3], [9, 2, 7]]
console.log(deleteGreatestValue(arr)) // [[1, 4], [2, 3], [2, 7]]
Enter fullscreen mode Exit fullscreen mode

49. 编写一个 sortPeople 函数,接受一个字符串数组 names 和一个数字数组 heights,其中 names[i] == heights[i]。该函数需要根据 heights 数组对 names 数组进行排序。

function sortPeople(names, heights) {
  const array = [];
  for (let [i, name] of names.entries()) {
    array.push([name, heights[i]]);
  }
  return array.sort((a, b) => b[1] - a[1]).map(([name]) => name);
}
const names = ['John', 'Maria', 'Alexa', 'Robert']
const heights = [180, 160, 165, 187]
console.log(sortPeople(names, heights)) // ['Robert', 'John', 'Alexa', 'Maria']
Enter fullscreen mode Exit fullscreen mode

50. 编写一个子集函数,该函数接受一个数字数组 nums,并返回这些数字的所有可能的数组变体。

function subsets(nums) {
  let result = [[]];

  for (let num of nums) { // Iterate through each number in the nums array
    const currentSize = result.length; // Get the current size of result to use it in the loop.

    for (let i = 0; i < currentSize; i++) {
      let subArray = [...result[i], num]; // Create a new subarray by adding the current number to the result[i] element.
      result.push(subArray);
    }
  }

  return result; // Return all possible variations of arrays from the numbers.
}
Enter fullscreen mode Exit fullscreen mode

51.如何反转链表?

让我们创建一个函数 reverseLinkedList,它将链接列表作为输入并返回该列表的反转版本。

方法:

  1. 它用 null 初始化结果变量,该变量将保存反转列表。
  2. 它用 head 初始化根变量,该变量指向列表的开头。
  3. 它进入一个 while 循环,持续直到 root 变为空,表示列表的结尾。
  4. 在循环内部,它会检查 result 是否已经包含元素。如果已经包含,它会创建一个新的列表节点,其当前值为 root.val ,并指向下一个节点 result 的指针。然后用这个新节点更新 result 。
  5. 如果 result 还没有任何元素,则创建一个新的列表节点,其当前值为 root.val,并将 null 作为指向下一个节点的指针。然后用这个新节点更新 result。
  6. 更新结果后,通过将 root.next 分配给 root,它移动到列表中的下一个元素。
  7. 一旦 while 循环完成,它将返回存储在结果中的反转列表。

总之,该函数通过从头到尾遍历每个节点来反转链接列表,为每个值创建一个新的列表节点并相应地更新指针。

/**
 * Definition for singly-linked list.
 * function ListNode(val, next) {
 *     this.val = (val===undefined ? 0 : val)
 *     this.next = (next===undefined ? null : next)
 * }
 */

function reverseLinkedList(node) {
  let result = null; // Initialize the result variable with null, it will hold the reversed list.
  let root = head; // Initialize the root variable with head, pointing to the start of the list.

  // While root is not null (until we reach the end of the list)
  while (root) {
    if (result) { // If result already has elements
      result = new ListNode(root.val, result); // Create a new list node with the current value root.val and a pointer to the next node result. Update result.
    } else { // If result doesn't have any elements yet...
      result = new ListNode(root.val, null); // Create a new list node with the current value root.val and null as the pointer to the next node. Update result.
    }
    root = root.next; // Move to the next element in the list.
  }
  return result; // Return the reversed list.
}
Enter fullscreen mode Exit fullscreen mode

52.如何对链表进行排序?

让我们创建一个函数 sortList,它将链接列表作为输入并返回该列表的排序版本。

方法:

  1. 检查给定的链接列表是否为空。
  2. 遍历链表并将节点值存储到数组中。
  3. 使用内置的 sort() 方法对数组进行排序。
  4. 使用排序后的数组创建一个新的链表。
  5. 返回所创建的链接列表的头。
/**
 * Definition for singly-linked list.
 * function ListNode(val, next) {
 *     this.val = (val===undefined ? 0 : val)
 *     this.next = (next===undefined ? null : next)
 * }
 */
function sortList (head) {
  if (!head) {
    return null;
  }
  let root = head;
  let arr = [];

  while(root){
    arr.push(root.val);
    root = root.next;
  }
  arr.sort((a, b) => a - b);

  let node = new ListNode(arr[0]);
  head = node;

  let temp = head;

  for(let i = 1; i < arr.length; i++){
    let node = new ListNode(arr[i]);
    temp.next = node;
    temp = temp.next;       
  }
  return head;
};
Enter fullscreen mode Exit fullscreen mode

结论

准备这些问题,学习相关主题,并查阅相关资源,可以提高你成功通过面试的机会。这篇文章是面试问题系列文章之一。

期待您的反馈和评论。
祝您面试顺利!

文章来源:https://dev.to/m_midas/52-frontend-interview-questions-javascript-59h6
PREV
基本算法
NEXT
使用 Express.js (Node.js) 构建登录和注销 API