记帮主成功的字节面试

假如您也和我一样,在准备春招。欢迎加我微信shunwuyu,这里有几十位一心去大厂的友友可以相互鼓励,分享信息,模拟面试,共读源码,齐刷算法,手撕面经。来吧,友友们!

前言

气温骤降,不知道帮主在北京会冷不?

帮主这一趟北上字节实习,大大的为我帮(IT帮 ,学校社团)涨了脸。就连一向对你冷淡的如烟妹子,也担心这北方的面食帮主能否适应。我这百度一面,被吓的不轻(面试:百度一面,吓尿了 - 掘金 (juejin.cn)),想要来京陪伴,可能没有那么快。如烟也在加快春招面试步伐,狠刷各家面经,叮嘱我把帮主的字节面经好好记录,好在帮里传扬。

记帮主成功的字节面试

好, 不贫了。我院IT帮帮主年前就成功拿下了字节offer,让协会里的我辈们甚是羡慕。帮主能去字节也在情理之中,他有着超强的学习力和专注的执行力。写这篇文章,记录下字节的面试题,也分享下他的成功经验。

良好的学习数据,赢得多家大厂的青睐

我和帮主并非985/双一流,要想去大厂,只能靠漂亮的学习数据,向面试官证明我们的实力。当然也得感谢字节并不是特别看中学校出身,只要简历漂亮,也给面。就喜欢字节这种宁愿错面一千,也不放过一个人才的风格。一起来努力准备春招吧,我坚信只要实力到位,就能去字节。帮主可能会是字节最后一批上市前拿期权 的同学了,到时如烟还不得以身相许?

掘金LV5

我们在和ChatGPT聊骚的时候,帮主就在用它辅助写技术文章了。往届学长跟我们分享过大厂比较看重技术写作者,善于总结、乐于分享。但想要达到大佬学长们LV5级,至少得大半年吧。帮主非常善于提出js高质量问题,借助AIGC的力量,文章写的又快又好。仅花了一个半月时间,就在掘金升到了LV5级。

这项数据在面试中帮了大忙,帮我内推百度的学长也说他leader之所以拿到简历就面我,也是因为几个关键数据,其中写作等级比较高就是其中之一。

掘金LV5级,肯定写了几十篇文章,而且需要蛮多社区同学给我们点赞,文章质量和深度应该可以。面试官基于这点,会觉得我们的基础不错,是个大厂的苗子。面试官也喜欢追着我们文章里的技术点提问,所以建议大家也把文章写起来,把握AIGC面试红利。面试官的理解还停留在之前的对技术写作者的考查,而我们已经利用AIGC的方式,两个多月就轻松达到了要求。

我们面大厂, 因为大厂青睐技术写作者,我们用好AIGC工具,不断提升提问题的能力,并通过文章分享给网友

leetcode 200+

大厂算法要求都比较高,建议leetcode 刷到200+。刷的时候可以囫囵吞枣,快一点,积累题量。面试前,重点刷常考题。推荐 动态规划终极绝杀! LeetCode:72.编辑距离_哔哩哔哩_bilibili

口才佳, 模拟面试

帮主口才极佳,在面试前, 组织过我们模拟面试,他当面试官。在这个过程中,他会考虑会面试官会怎么出题,想听怎么样的回答,以及会在什么点上不停的追问。然后再反向,调整自己的面试,并在面试中把握主动。

时机

帮主时机选择比较好,春招竞争还是比较大的,多少人竞争一个岗位,听学长说字节的竞争比例大概是200:1,跟公务员差不多(工资是公务员的n倍)。春招要先笔试,再面试,还要在众多候选人里不断那啥。面试官心态也不会急于成交,想看看后面还有没有更好的(好渣啊)。帮主的年前面试简单很多,一不要笔试,二是竞争的人不多(在过年或觉得没准备好),三是今年事今年闭,结果出来的比较快。要是我也提前半个月面试,说不定百度也可能在年前拿下了。

以上是我对帮主拿下字节的关键点分析,欢迎各位留言区一起讨论,补充。接下来,列出帮主的面试真题,一起学习。

字节面试题

自我介绍

大家可以查看面试:百度一面,吓尿了 - 掘金 (juejin.cn),里面介绍了敖丙大佬自我介绍公式:我是谁+从哪里来+我做过什么+有什么成绩+为什么能胜任。

实现Sum函数链式调用计算多数之和

  • 原型链

    最直接的写法就是利用原型链+add方法返回this来实现,total和作为对象的属性来访问的返回

js 复制代码
function Sum(initialValue=0) {
    this.total = initialValue;
}
Sum.prototype.add = function(num) {
    this.total += num
    return this;
}

const result = new Sum(1) // 输出10
result.add(2).add(3);
console.log(result.total)
  • 闭包
js 复制代码
function sum(...args) {
  // 初始总和值
  let currentSum = args.reduce((acc, val) => acc + val, 0);

  // 定义 sumof 函数
  function sumof(...nextArgs) {
    // 将当前总和值与新传入的参数相加
    currentSum += nextArgs.reduce((acc, val) => acc + val, 0);
    // 返回 sumof,以支持链式调用
    return sumof;
  }

  // 添加 sumof 方法,用于获取最终总和值
  sumof.sumof = function () {
    return currentSum;
  };

  // 返回 sumof 函数,以支持初始调用 sum(1, 2)
  return sumof;
}

// 示例使用
const result = sum(1, 2)(3).sumof();
console.log(result); // 输出:6

闭包+递归,避开原型链实现链式调用,再增加了一个sumof.sumof方法专门返回和。es6 展开运算符和reduce用的挺好的, 大家可以仔细玩下。

  • 闭包 + 隐式转换
js 复制代码
function sum(initialValue = 0) {
    // 初始值
    let total= initialValue;
    // 闭包函数,一直对自由变量sum做累加
    function add(number) {
      total += number;
      // 返回函数本身,实现链式
      return add;
    }
    // 当add 触发隐式转换时,valueOf就会执行,返回自由变量,拿到累加和
    add.valueOf = function() {
      return total;
    };
    // 返回add 形成闭包, 外部可以调用
    return add;
  }
  
  const result = sum(1)(2)(3)(4); // 输出10
  console.log(+result); // 输出10 +触发隐式类型转换

闭包递归完美实现函数链式调用,再冷不防来个value, 直接隐式类型转换就拿到和了。比在add 上添加静态属性或给sum加数量参数,高级的多。

result是函数,在JS中函数就是对象。+result会触发隐式类型转换,想将result转化为原始值(primitive),就会调用valueOf方法。正是利用这个机制,在valueOf方法里返回闭包中的自由变量。

编写函数,每次返回下一个质数(Prime number)

质数(也称为素数)是一个大于1的自然数,其特征是除了1和它本身以外,不能被其他任何自然数整除。换句话说,一个质数只有两个正因数:1和该质数本身。 2是最小的质数,因为它只能被1和2整除。3、5、7、11、13、17、19等都是质数,因为它们各自只能被1和自身整除。

这也是一道大厂的热门题,考查闭包(下一个,自由变量)和质数判断函数的封装。

js 复制代码
function isPrime(num) { 
    // 除了1和本身, 都不能整除 
    for (let i = 2; i < num; i++) { 
        if (num % i === 0) { return false; } 
    } 
    // 返回true  排除1
    return num > 1; 
} 
function getPrime() { 
    let currentNumber = 2; 
    // 起始数字 
    return function () { 
        while (true) { 
            if (isPrime(currentNumber)) { 
                const prime = currentNumber; 
                // 闭包自增
                currentNumber++; 
                return prime; 
            } 
            currentNumber++; 
        } 
    }; 
}  

const getNextPrime = getPrime(); 
console.log(getNextPrime()); // 输出:2 console.log(getNextPrime()); // 输出:3 console.log(getNextPrime()); // 输出:5
console.log(getNextPrime()); // 输出:7
console.log(getNextPrime()); // 输出:9
....

列表转树

经典考题,列表转树,在多种业务开发场景中有这个需求。新手业务经验少,可能没遇到过;或者只是有幸刷到这道题,除代码处,没啥业务感觉。所以将编程答题跟业务经验表达,边coding边聊是个不错的想法!

可以提及以下业务列表:

  • 文件目录结构展示
  • 组织架构管理与展示
  • 多级菜单生成
  • 地区行政划分展示
  • 评论回复层级构建

可组装成树的列表(数组)一般有以下几个要求

  • id 当前节点id
  • parentId 当前节点的父节点id
  • children 组成树时加上

初始数组:

树状对象:

遍历数组,逐一找到子节点添加到父节点中

js 复制代码
function listToTreeSimple(data) {
 const res = [];
 data.forEach((item) => {
    // 当到当前节点的父节点
   const parent = data.find((node) => node.id === item.parentId);
   if (parent) {
     parent.children = parent.children || [];
     parent.children.push(item);
   } else {
     // * 根节点
     res.push(item);
   }
 });
 return res;
}

两重循环开销比较大, 我们使用哈希表来优化一下:

ini 复制代码
function listToTree(data) {
  // * 先生成parent建立父子关系
  const obj = {};
  data.forEach((item) => {
    obj[item.id] = item;
  });
  // * obj -> {1001: {id: 1001, parentId: 0, name: 'AA'}, 1002: {...}}
  // console.log(obj, "obj")
  const parentList = [];
  data.forEach((item) => {
    const parent = obj[item.parentId];
    if (parent) {
      // * 当前项有父节点
      parent.children = parent.children || [];
      parent.children.push(item);
    } else {
      // * 当前项没有父节点 -> 顶层
      parentList.push(item);
    }
  });
  return parentList;
}

性能提升是很显著的,我们可以向面试官提出cosole.time()console.timeEnd()这对好用的性能调试工具。

递归法

这道考题主要想考查的就是递归的编程能力。

js 复制代码
function listToTree(list, parentId) {
  const tree = [];
  
  // 遍历列表
  for (let i = 0; i < list.length; i++) {
    if (list[i].parent_id === parentId) {
      // 创建一个节点对象
      const node = {
        id: list[i].id,
        name: list[i].name,
        children: listToTree(list, list[i].id) // 递归调用,查找当前节点的子节点
      };
      
      // 将节点添加到树中
      tree.push(node);
    }
  }
  
  return tree;
}

// 示例数据
const list = [
  { id: 1, name: 'Node 1', parent_id: null },
  { id: 2, name: 'Node 2', parent_id: 1 },
  { id: 3, name: 'Node 3', parent_id: 1 },
  { id: 4, name: 'Node 4', parent_id: 2 },
  { id: 5, name: 'Node 5', parent_id: 2 },
  { id: 6, name: 'Node 6', parent_id: 3 },
  { id: 7, name: 'Node 7', parent_id: 3 },
  { id: 8, name: 'Node 8', parent_id: 4 }
];

// 调用函数进行转换
const tree = listToTree(list, null);

// 输出结果
console.log(JSON.stringify(tree, null, 2));

面试官一上来,就三道编程题,字节还是烦难面的,需要有两把刷子。接下来面试官主要围绕项目来提了些问题

项目

针对全栈项目问了前后端登录流程

  • JWT JSON Web Tokens

    Authorization: Bearer JWT 取代cookie

    axios 请求拦截中设置HTTP Header Authorization 字段

    js 复制代码
    ...
    instance.interceptors.request.use(config => {
        
     if (token) {
      config.headers.Authorization = `Bearer ${token}`;
    }
    return config;
    })
    • token生成和校验

    安装jsonwebtoken

    js 复制代码
    npm install jsonwebtoken

    生成token

    js 复制代码
    const jwt = require('jsonwebtoken'); 
    // 定义 secret key,用于签名 token 
    const secretKey = 'your-secret-key'; 
    // 在生产环境中应存储于安全位置 比如放到.env中
    // 用户信息
    const user = { id: 1, username: 'exampleUser' }; 
    // 生成 token 
    function generateToken(user) { 
    const payload = { 
        ...user, 
        exp: Math.floor(Date.now() / 1000) + (60 * 60) 
    }; // 设置过期时间(这里为一小时后) 
    return jwt.sign(payload, secretKey); 
    } // 使用 
    const token = generateToken(user); 
    console.log(token);

    验证token

    js 复制代码
    // 验证 token 
    function verifyToken(token) { 
        return new Promise((resolve, reject) => { 
            jwt.verify(token, secretKey, (err, decoded) => { 
                if (err) { return reject(err); } 
                resolve(decoded); // 解码后的用户信息 
            }); 
        }); 
    } 
  • Vue.js前端登录流程

    描述用户输入账号密码后,Vue前端如何通过HTTP请求(如axios)向后端发起登录请求,并携带必要的认证信息

    登录成功后,如何接收并存储后端返回的JWT(通常存储在浏览器的localStorage或sessionStorage中)

  • 前端路由守卫

js 复制代码
// 在Vue应用的初始化文件(如 main.js)中配置Vue Router 
import Vue from 'vue'; 
import VueRouter from 'vue-router'; 
import jwt_decode from 'jwt-decode'; // 用于解码JWT 
import store from './store'; // 假设你有一个Vuex store来管理登录状态等信息 
const routes = [ /* 这里是你的路由配置 */ ];
const router = new VueRouter({ routes, }); 
// 全局前置导航守卫 
router.beforeEach((to, from, next) => { 
const token = localStorage.getItem('token'); 
// 假设JWT存储在本地存储中 
let decodedToken; 
if (token) { 
    try { 
        decodedToken = jwt_decode(token); // 检查token是否过期或其他自定义逻辑 
        if (decodedToken.exp * 1000 < Date.now()) { 
            store.dispatch('logout'); 
            // 如果过期,触发登出操作并清除状态 
        } else { 
        // 如果token有效,则继续导航并可能将用户信息存入store 
        store.commit('setUser', decodedToken); 
        } 
    } catch (err) { 
    // JWT格式错误或无法解码,处理错误情况 
    store.dispatch('logout');
} } // 根据用户的登录状态决定跳转路径 
if (to.meta.requiresAuth && !decodedToken) { 
// 若目标路由需要认证且用户未登录 
next({ name: 'Login' }); 
// 重定向到登录页 
} else { next(); 
// 否则正常进行路由切换 
} 
}); 
  • 相比于cookie, JWT的优点
特性 JWT(JSON Web Tokens) Cookie
自包含性 JWT包含了所有必要的用户信息,并且可以携带权限数据。服务器无需查询数据库即可验证和理解令牌内容。 Cookie通常只存储会话ID,服务器需要根据会话ID查询数据库来获取用户状态。
无状态性 服务端无须保存任何会话状态,减轻了服务器负担,有利于横向扩展。 服务端需要维护会话存储,如Session,增加了服务器开销和复杂度。
跨域支持 JWT可以通过Authorization头发送,不受同源策略限制,因此适用于微服务架构或多个子域名的场景。 Cookie受到同源策略约束,进行跨域请求时需处理CORS问题。
安全性 可以通过HTTPS传输,并对JWT内容进行签名(Signature)或加密(Encryption),保护数据安全。虽然JWT默认不加密,但可以根据需求设置。 Cookie可通过HTTPOnly、Secure等属性增强安全性,但仍然有被截获的风险。
大小限制 JWT相对较小,适合于移动设备等带宽受限环境,但也需要注意payload大小不宜过大。 Cookie的大小有限制,过大会影响性能。
生命周期控制 JWT中包含过期时间(exp),可以精确控制 token 的有效时间。 Cookie的有效时间可以通过Expires或Max-Age属性控制,但不如JWT中的exp字段直观。
可扩展性 JWT标准定义清晰,易于与其他系统集成,同时支持自定义claim进行扩展。 Cookie的扩展性较低,主要用于简单的会话管理。

项目亮点

帮主介绍了以下几点,大家可以根据这些技术点,去看在相应链接。

总结

一面到这里就结束了,面试官挺满意的。当天下午就通知一面过了。如果您觉得文章还不错,欢迎点赞收藏,下编记录帮主成功的字节二面。

欢迎加微信shunwuyu, 一起加油24年春招!

参考资料

相关推荐
恋猫de小郭29 分钟前
Flutter Zero 是什么?它的出现有什么意义?为什么你需要了解下?
android·前端·flutter
崔庆才丨静觅7 小时前
hCaptcha 验证码图像识别 API 对接教程
前端
passerby60618 小时前
完成前端时间处理的另一块版图
前端·github·web components
掘了8 小时前
「2025 年终总结」在所有失去的人中,我最怀念我自己
前端·后端·年终总结
崔庆才丨静觅8 小时前
实用免费的 Short URL 短链接 API 对接说明
前端
崔庆才丨静觅8 小时前
5分钟快速搭建 AI 平台并用它赚钱!
前端
崔庆才丨静觅9 小时前
比官方便宜一半以上!Midjourney API 申请及使用
前端
Moment9 小时前
富文本编辑器在 AI 时代为什么这么受欢迎
前端·javascript·后端
崔庆才丨静觅9 小时前
刷屏全网的“nano-banana”API接入指南!0.1元/张量产高清创意图,开发者必藏
前端