# 一、前言
先说一下结果吧 —— 一面秒挂。
这个是我暑期实习的第一次面试,为啥过了这么久才发?当然是我的面试都结束了,现在终于可以抱着轻松的心态复盘一下了!!
我个人感觉自己确实是有很多的不足的地方,对于 Promise 的相关知识以及算法的部分掌握的实在是太不牢固了。在这里先记录一下吧。
# 二、题目列表
-
上来老三样,自我介绍 + 询问实习时间地点要求 + 询问我的项目经历。由于我在简历里面写了挺多竞赛相关的项目,让他误以为我是一个超级全能的全干工程师,然后就让我挑一个比较有难度的项目来讲。
于是我就挑了一个目前正在参与的开源项目:Create-Neat,并详细讲了一下我当初负责的TemplateAPI
的设计。这个就相当于是偏纯技术的一个项目,因此和面试官聊的还比较多。
这个项目聊完了之后,又跟我聊了一个我在目前公司负责的 Markdown 渲染器的设计与实现,这个也聊的比较久。 -
代码题:JavaScript 事件循环,给我一段代码,让我判断 console.log () 打印输出的顺序。这个题目相对比较经典 + 我没有录屏这边就不写原题了。
-
代码题:实现一个
sleep()
函数,使用async/await
,可以向其中传入一个参数dalay
用于控制其函数内部console.log()
触发的延迟时间。具体代码如下:const func = async () => {
await sleep(3);
console.log("Hello World");
};
const sleep = (delay) => {
// 编写函数体
};
这道题实际上比较简单,就是利用了
Promise
的机制,我们可以把await
语句下面的代码看成Promise.then()
回调内部的代码,我们只要在sleep
函数里面返回一个Promise
,然后在 delay 秒之后把其状态变为fulfilled
就可以了。解法代码如下:
const func = async () => {
await sleep(3);
console.log("Hello World");
};
const sleep = (delay) => {
return new Promise((resolve) => {
setTimeout(resolve, delay * 1000);
});
};
-
代码题:第三题的进阶版。
首先有一个已经写好的接口:
getUserInfo
,它接受一个参数userId
,在内部通过axios
发起请求,返回一个Promise
对象。目前有一个新的需求:实现一个新的函数用来二次封装这个
getUserInfo
,同样也返回一个Promise
对象,如果正常返回成功 / 失败,那么就正常的触发成功 / 失败的回调;如果请求时长超过了 10s,那么就需要自动触发报错,不管原来的这个接口接下来是否请求成功 / 失败。当时这道题有点懵,想了半天没有想出来,因为之前这种超时报错的处理基本 100% 全都是交给
axios
的响应拦截器做的处理,还没有自己手动实现过。接下来给一下我事后实现的一个 TS 版本的源码,给大家参考一下:
import axios from "axios";
function getUserInfo(userId: string): Promise<any> {
return axios.get(`/api/user/${userId}`);
}
function getUserInfoWithTimeout(userId: string): Promise<any> {
return new Promise((resolve, reject) => {
const timeout = setTimeout(() => {
reject(new Error("请求超时"));
}, 10000); // 设置 10 秒超时
getUserInfo(userId)
.then((response) => {
clearTimeout(timeout); // 清除超时计时器
resolve(response.data);
})
.catch((error) => {
clearTimeout(timeout); // 清除超时计时器
reject(error);
});
});
}
// 示例用法
getUserInfoWithTimeout("123")
.then((data) => {
console.log("成功:", data);
})
.catch((error) => {
console.error("失败:", error.message);
});
-
代码题:判断
this
作用域。具体题目如下:
var num = 123;
var obj = {
num: 456,
getNum: function () {
var num = 789;
return this.num;
},
};
console.log(obj.getNum());
让我判断最终打印输出的结果为多少。
实际上,
getNum
方法被调用时,会返回this.num
。在这个上下文中,this
指向了obj
对象,因此this.num
实际上是指向了obj
对象中的num
属性。根据 JavaScript 的作用域链规则,如果当前作用域中找不到某个变量,就会向外层作用域查找,直到找到或者查找到最外层作用域为止。在
getNum
方法内部,虽然有一个同名的num
变量(var num = 789;
),但在返回语句return this.num;
中,this.num
实际上是指向了obj
对象中的num
属性,而不是方法内部的num
变量。因此,最终打印输出的结果是456
。 -
代码题:大数相加。简单来说,实现一个函数,内部可以传递两个无限长的字符串,而这两个字符串全都是由数字字符组成,在这个函数中实现这两个字符串数字的相加,最终返回的也是一个由数字组成的字符串。当时由于前两题发挥不太好导致心情崩溃了,结果没有写出来。
这里给一下之后写的一个解法:
function addStrings(num1: string, num2: string): string {
let carry = 0; // 进位
let result = ""; // 结果字符串
let i = num1.length - 1;
let j = num2.length - 1;
// 从个位开始逐位相加
while (i >= 0 || j >= 0 || carry > 0) {
const digit1 = i >= 0 ? parseInt(num1[i]) : 0;
const digit2 = j >= 0 ? parseInt(num2[j]) : 0;
const sum = digit1 + digit2 + carry;
// 计算当前位的结果,并更新进位
const digitSum = sum % 10;
carry = Math.floor(sum / 10);
// 将当前位的结果添加到结果字符串的开头
result = digitSum.toString() + result;
// 更新索引
i--;
j--;
}
return result;
}
-
反问阶段。
# 三、结语
我是第一次遇见八股一个都不问,项目问完之后直接开始拷打算法的面试。我之前辛辛苦苦准备了这么久的八股一个都没用上,还偏偏是考我最薄弱的算法部分,实在是令人忍俊不禁。
嘛,不过这也基本确定了我本人的算法还是不太行,得好好加油,平时除了写业务之外,得把刷算法也当成家常便饭呀!!