记帮主成功的字节面试

假如您也和我一样,在准备春招。欢迎加我微信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年春招!

参考资料

相关推荐
QGC二次开发4 分钟前
Vue3 : Pinia的性质与作用
前端·javascript·vue.js·typescript·前端框架·vue
云草桑15 分钟前
逆向工程 反编译 C# net core
前端·c#·反编译·逆向工程
布丁椰奶冻21 分钟前
解决使用nvm管理node版本时提示npm下载失败的问题
前端·npm·node.js
AntDreamer35 分钟前
在实际开发中,如何根据项目需求调整 RecyclerView 的缓存策略?
android·java·缓存·面试·性能优化·kotlin
Leyla1 小时前
【代码重构】好的重构与坏的重构
前端
影子落人间1 小时前
已解决npm ERR! request to https://registry.npm.taobao.org/@vant%2farea-data failed
前端·npm·node.js
世俗ˊ1 小时前
CSS入门笔记
前端·css·笔记
子非鱼9211 小时前
【前端】ES6:Set与Map
前端·javascript·es6
6230_1 小时前
git使用“保姆级”教程1——简介及配置项设置
前端·git·学习·html·web3·学习方法·改行学it
想退休的搬砖人1 小时前
vue选项式写法项目案例(购物车)
前端·javascript·vue.js