腾讯前端开发校招一面笔试题

949 阅读11分钟

前言

小编我自从360实习归来之后也是马不停蹄的开始了校招的找工作,没有停下学习的脚步,前端时间投了许多简历也约到了不少的公司面试,唯独就是迟迟约不到大厂的面试,兴许是快过年了大家都已经准备收工回家过年了,随着大年三十越来越近,大家回家的心也归心似箭,年味也越来越显,可惜我还是一个没有找到工作的孩子哇呜呜呜(欲哭无泪),没伞的孩子只能努力奔跑......前几天也是终于约到了一家大厂腾讯的前端开发校招面试,面试前的一段时间还是十分紧张,不停的在刷算法背八股复习面试题,心里就像悬着一块大石头,就怕面试的时候一问三不知,到时候就尴尬了......面完后,悬着的心终于死掉了,感觉被吊打了,裂开,果然顶级大厂腾讯的面试就是不一样,问的不仅深还广,可谓是全方位拷打,面完人已经麻了。这不马上就给大家来分享一下我的面试经验。

腾讯面试官上来就是让我当场手撕代码题,给了我6道题目,限时20分钟,让我20分钟内写完。我听完当头就是一棒,what?!! 20分钟?六道算法题??这是能干的事吗?这也太看得起我了吧,人已经傻了,已老实。呆了1分钟后也是开始了大脑的运转,最后也是争气的写完了4题半,到了20分钟面试官也是打断我说时间到了,开始看看我的作答了。看完后感觉面试管也是觉得还可以的样子,也没有说我啥,就继续开始的一个小时的面试拷打环节。今天这篇文章先和大家分享一下腾讯的这六道笔试代码题。下文在和大家分享一下面试的题目。

笔试题

题目一

有字符串 var = 'abc345efgabcab',请写出 3 条 JS 语句分别实现如下 3 个功能(使用正则):

1)去掉字符串中的a、b、c 字符,形成结果:'345efg'

2)将字符串中的数字用中括号括起来,形成结果:'abc[345]efgabcab'

3)将字符串中的每个数字的值分别乘以 2,形成结果:'abc6810efgabcab'

这道题考察的并不难,就是考察大家对正则表达试的理解和应用,如果你会正则表达式的话,是可以很快写出来的

1)

解答
var str='abc345efgabcab'
1)
var res=str.replace(/[abc]/g,'')
console.log(res)
2)
var res2=str.replace(/\d+/g,function(match){
    return '['+match + ']'
})
console.log(res2)
3)
var res3=str.replace(/\d/g,function(match){
    return (parseInt(match)*2).toString()
})
console.log(res3)

像第三小问就是

  • 使用正则表达式 /\d/g 匹配每个单独的数字字符。
  • 通过回调函数将每个数字字符转换为整数并乘以 2,然后转换回字符串。
  • 最终输出结果为 "abc6810efgabcab"

image-20250125174928099

可以看到结果也是没有什么问题的。

题目二

请写出以下程序的输出

const arr = [3, 1, 4, 1, 5, 9]; const sortedArr = arr.sort((a, b) => a - b); console.log(sortedArr); console.log(arr);

这道题很简单,就是考察对数组的排序方式以及对sort方法的理解,sort 方法会直接修改原数组,并返回排序后的数组。参数 (a, b) => a - b 是一个比较函数,用于实现升序排序知道了这些就很容易知道输出的结果都是1,1,3,4,5,9

image-20250125175728493

题目三

多种方式实现多层嵌套数组扁平化

这种题目也是笔试和面试当中常考的题目,大家也需要十分清楚。我当时写出了两种一种是使用api flat()方法,另一种是使用递归

使用flat()

const arr = [1, 2, [3, 4, [5]]];
//数组的扁平化
const newArr = arr.flat(Infinity);
console.log(newArr); 

这里我就用flat(Infinity),这个就代表不管你是几维数组,我都给你降成一维数组

使用递归

递归是一种常见的实现数组扁平化的方法。它的思路是通过递归遍历数组里的所有元素,如果遇到数组,则继续递归处理,直到将所有元素都放入一个新的一维数组中。具体实现方式有直接递归和reduce()方法两种。

直接递归:

直接递归的实现就是通过循环遍历数组元素,判断当前元素是否为数组,如果是,则递归调用扁平化函数,如果不是,则将其加入到新的一维数组中。

const arr = [1, 2, [3, 4, [5]]];
//使用递归来解决
function flatten(arr) {
    let res = [];
    for (let i = 0; i < arr.length; i++) {
        if (Array.isArray(arr[i])) {
            //res=res.concat(flatten(arr[i]));
            res = [...res, ...flatten(arr[i])]
        } else {
            res.push(arr[i]);
        }
    }
    return res;
}
const newArr = flatten(arr);
console.log(newArr);
  1. 在循环体内,用Array.isArray(arr[i])判断当前元素是否为数组。

    如果当前元素arr[i]是一个数组,则对该子数组进行递归调用flatten(arr[i]),将扁平化后的结果与res数组合并。这里可以使用扩展运算符...来展幵数组,等价于使用res = res.concat(flatten(arr[i]))。这两种方法效果是一样的。

    如果当前元素不是一个数组,则直接将该元素arr[i] push到结果数组res中。

递归 配合reduce()

reduce()方法也可以遍历数组的所有元素,还能将他们累加起来

const arr = [1, 2, [3, 4, [5]]];
function flatten(arr) {
   return arr.reduce((pre,item) => {
    return pre.concat(Array.isArray(item)? flatten(item) : item)//[1,2],[3,4],[5]
    },[])
}
console.log(flatten(arr));

flatten函数内部,使用了数组的reduce方法。reduce方法会对数组中的每个元素执行一个提供的 reducer 函数(升序执行),最终结果是将数组简化为单一的输出值。reduce 的目的是将多维数组扁平化为一维数组。

  • reduce的第一个参数是一个回调函数,该回调函数接收两个参数:累计器pre(previousValue)和当前元素item(currentValue)。

  • 回调函数内部,首先检查item是否为数组(Array.isArray(item)`):

    如果item是数组,就对这个子数组递归调用flatten函数,然后再与累计器pre进行拼接(pre.concat(flatten(item)))。这样可以处理任意深度的嵌套数组。

    如果item不是一个数组,直接将其添加到累计器pre中。

  • reduce的第二个参数是一个初始值[],即累计器pre的初始状态,表示扁平化结果的起始为空数组。

如果还想了解更多方法的大家可以去看看我之前的这篇文章,介绍了5种数组扁平化的方法=>面试热点:数组扁平化的五种方法前言 在咱们面试的过程中,很容易会被面试官问到这样一道题:请问你如何实现一个数组的扁平化呢 - 掘金 (juejin.cn)

题目四

实现promise.All 函数

果然,终究还是逃不过面试官的手撕代码拷打,我这题只写了一半,不过面试官没有刁难我,就问了我思路和想法,我也是都说了出来,

function promiseAll(promises) {
  return new Promise((resolve, reject) => {
    // 如果输入不是数组,直接 reject
    if (!Array.isArray(promises)) {
      return reject(new TypeError('Argument must be an array'));
    }

    const results = []; // 存储每个 Promise 的结果
    let completedCount = 0; // 记录已完成的 Promise 数量

    // 遍历 Promise 数组
    promises.forEach((promise, index) => {
      // 将非 Promise 值转换为 Promise
      Promise.resolve(promise)
        .then((result) => {
          results[index] = result; // 将结果存入对应位置
          completedCount++;

          // 如果所有 Promise 都完成,resolve 结果数组
          if (completedCount === promises.length) {
            resolve(results);
          }
        })
        .catch((error) => {
          // 如果任何一个 Promise 失败,直接 reject
          reject(error);
        });
    });

    // 如果传入空数组,直接 resolve 空数组
    if (promises.length === 0) {
      resolve(results);
    }
  });
}
// 测试用例 1:所有 Promise 成功
const p1 = Promise.resolve(1);
const p2 = Promise.resolve(2);
const p3 = Promise.resolve(3);

promiseAll([p1, p2, p3])
  .then((results) => {
    console.log(results); // 输出: [1, 2, 3]
  })
  .catch((error) => {
    console.error(error);
  });

// 测试用例 2:包含一个失败的 Promise
const p4 = Promise.resolve(4);
const p5 = Promise.reject('Error occurred');
const p6 = Promise.resolve(6);

promiseAll([p4, p5, p6])
  .then((results) => {
    console.log(results);
  })
  .catch((error) => {
    console.error(error); // 输出: "Error occurred"
  });

// 测试用例 3:非数组参数
promiseAll('not an array')
  .then((results) => {
    console.log(results);
  })
  .catch((error) => {
    console.error(error); // 输出: TypeError: Argument must be an array
  });

// 测试用例 4:空数组
promiseAll([])
  .then((results) => {
    console.log(results); // 输出: []
  })
  .catch((error) => {
    console.error(error);
  });

后面面试官又让我介绍一下promise.all()、promise.allSetted()、promise.race(),

race()我记得,但是allSetted()我是真的忘了(想哭)

promise.allSetted()是接收一个 Promise 数组,等待所有 Promise 完成(无论成功或失败),并返回一个包含每个 Promise 结果的对象数组。

promise.race()接收一个 Promise 数组,返回第一个完成(无论成功或失败)的 Promise 的结果。

promise.all()接收一个 Promise 数组,当所有 Promise 都成功时返回一个包含所有结果的数组;如果任何一个 Promise 失败,则立即返回失败的结果。

面完后我专门去写了一篇文章整理了promise的所有方法,大家有兴趣的可以看看我的这篇文章=>promise的方法总结Promise 是 JavaScript 中用于处理异步操作的对象。它表示一个异步操作的最终完成 - 掘金 (juejin.cn)

题目五

js实现大数相加

这道题我没写出来主要是时间不够了,我写了最后一题第六题,这题就没时间了,不过当时和面试官说了一下大概思路他也是比较满意的。

在 JavaScript 中,数字的精度有限,最大安全整数是 2^53 - 1(即 9007199254740991)。如果超过这个范围,直接使用 + 运算符会导致精度丢失。因此,对于大数相加,我们需要将数字作为字符串处理,逐位相加并处理进位。

主要思路就是这样:

  1. 将数字作为字符串处理。
  2. 反转字符串,方便从低位到高位逐位相加。
  3. 逐位相加并处理进位。
  4. 反转结果,得到最终的大数相加结果。
function addBigNumbers(num1, num2) {
  // 将两个数字字符串反转,方便从低位到高位逐位相加
  let arr1 = num1.split('').reverse();
  let arr2 = num2.split('').reverse();
  let result = []; // 存储结果
  let carry = 0; // 进位

  // 遍历较长的数组
  for (let i = 0; i < Math.max(arr1.length, arr2.length); i++) {
    // 获取当前位的数字,如果不存在则默认为 0
    const digit1 = arr1[i] ? parseInt(arr1[i]) : 0;
    const digit2 = arr2[i] ? parseInt(arr2[i]) : 0;

    // 计算当前位的和(包括进位)
    const sum = digit1 + digit2 + carry;

    // 计算当前位的结果和新的进位
    result.push(sum % 10);
    carry = Math.floor(sum / 10);
  }

  // 如果最后还有进位,添加到结果中
  if (carry > 0) {
    result.push(carry);
  }

  // 将结果反转并转换为字符串
  return result.reverse().join('');
}
// 测试用例 1:普通大数相加
const num1 = '12345678901234567890';
const num2 = '98765432109876543210';
console.log(addBigNumbers(num1, num2)); // 输出: "111111111011111111100"

// 测试用例 2:一个大数和一个小数相加
const num3 = '99999999999999999999';
const num4 = '1';
console.log(addBigNumbers(num3, num4)); // 输出: "100000000000000000000"

// 测试用例 3:两个小数相加
const num5 = '123';
const num6 = '456';
console.log(addBigNumbers(num5, num6)); // 输出: "579"

// 测试用例 4:包含前导零的数字
const num7 = '000123';
const num8 = '000456';
console.log(addBigNumbers(num7, num8)); // 输出: "579"

题目六

编写二叉树的前序遍历函数(简写)

这道题很简单,如果平常会刷二叉树的算法的话这题应该都能很快写出来的,这里我就不多说了,

var preorderTraversal = function(root) {
    const res=[];//用于存储遍历的结果
    preorder(root,res);
    return res;
};
//设置函数用于递归遍历
const preorder=(root,res)=>{
    if(root===null) return;//当前节点为空时,无需进行递归
    res.push(root.val)//记录根节点值
    preorder(root.left,res);//前序遍历左子树
    preorder(root.right,res);//前序遍历右子树
}

总结

这六道笔试题坐下来整体感觉不是很难,主要就是时间不够,20分钟需要写完,我只写了4题半,我还是写的太慢了,需要多练练多沉淀沉淀。果然腾讯的面试就是不一样,上来就是手撕代码。还得加油练,今天的分享就先到这里了,下文在和大家分享一下被腾讯面试官拷打的一个小时。已老实。