前言
相信很多的小伙伴都还在经历春招、秋招找工作的痛苦之中,那么小编也曾是万千应届求职者之一,也曾经历一天多轮笔面试 ,还要下楼做核酸的痛苦之中。小编满怀着对年轻人的殷切期望,希望通过这篇文章为前端求职的同学提供一定的帮助
看到小编投递的简历就能发现,我的投递直到10月23日才停止,很多面试也是在10月23日之后才正式开始的,尤其是一些银行、国企单位,所以暂时还没拿到offer的小伙伴们耐心等待,耐心准备面试、笔试,你的好消息总会在春暖花开的日子到来
(附上我当时投递的公司列表)
三、如何准备面试回答?
小编将自己的经验总结成了以下这些点
3.1 回答要有逻辑与递进
例如:面试官问你:什么是Promise,说说你的理解?
错误案例: 想到Promise的啥特性就说啥,随心所欲
正确回答: 三步走策略(是什么、为什么、怎么做)
Promise是什么: 就说一说Promise是目前的异步解决方案,一开始是社区方案,ES6新增了Promise.......(主要是说Promise的前世今生,为的是凸显你的知识范围比较广泛,会关注技术的背景)
为什么要用Promise: 就说一说,Promise能够解决异步回调地狱。把回调地狱的问题简单说一下。然后开始阐述Promise是怎么解决回调地狱,这里你就需要认真研究Promise实现的原理,最好是看过Promise的demo源码。
Promise怎么用: 你就开始阐述resolve、reject、all、race等方法,小编建议这里,你一定要了解过Promise的demo源码,你就和面试官说resolve、reject方法的实现,一定记得从代码的角度说(要知道一个能认真研究某个新特性demo源码的同学的水平,对于面试官来说,一定是对你刮目相看)
案例分析:
- 这是我当时准备的Promise的问题,都是基于我认真看过它的demo源码总结出来的
- 这是我整理的demo源码
// 模拟实现Promise
// Promise利用三大手段解决回调地狱:
// 1. 回调函数延迟绑定
// 2. 返回值穿透
// 3. 错误冒泡
// 定义三种状态
const PENDING = 'PENDING'; // 进行中
const FULFILLED = 'FULFILLED'; // 已成功
const REJECTED = 'REJECTED'; // 已失败
class Promise {
constructor(exector) {
// 初始化状态
this.status = PENDING;
// 将成功、失败结果放在this上,便于then、catch访问
this.value = undefined;
this.reason = undefined;
// 成功态回调函数队列
this.onFulfilledCallbacks = [];
// 失败态回调函数队列
this.onRejectedCallbacks = [];
const resolve = value => {
// 只有进行中状态才能更改状态
if (this.status === PENDING) {
this.status = FULFILLED;
this.value = value;
// 成功态函数依次执行
this.onFulfilledCallbacks.forEach(fn => fn(this.value));
}
}
const reject = reason => {
// 只有进行中状态才能更改状态
if (this.status === PENDING) {
this.status = REJECTED;
this.reason = reason;
// 失败态函数依次执行
this.onRejectedCallbacks.forEach(fn => fn(this.reason))
}
}
try {
// 立即执行executor
// 把内部的resolve和reject传入executor,用户可调用resolve和reject
exector(resolve, reject);
} catch(e) {
// executor执行出错,将错误内容reject抛出去
reject(e);
}
}
then(onFulfilled, onRejected) {
onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : value => value;
onRejected = typeof onRejected === 'function'? onRejected :
reason => { throw new Error(reason instanceof Error ? reason.message : reason) }
// 保存this
const self = this;
return new Promise((resolve, reject) => {
if (self.status === PENDING) {
self.onFulfilledCallbacks.push(() => {
// try捕获错误
try {
// 模拟微任务
setTimeout(() => {
const result = onFulfilled(self.value);
// 分两种情况:
// 1. 回调函数返回值是Promise,执行then操作
// 2. 如果不是Promise,调用新Promise的resolve函数
result instanceof Promise ? result.then(resolve, reject) : resolve(result);
})
} catch(e) {
reject(e);
}
});
self.onRejectedCallbacks.push(() => {
// 以下同理
try {
setTimeout(() => {
const result = onRejected(self.reason);
// 不同点:此时是reject
result instanceof Promise ? result.then(resolve, reject) : resolve(result);
})
} catch(e) {
reject(e);
}
})
} else if (self.status === FULFILLED) {
try {
setTimeout(() => {
const result = onFulfilled(self.value);
result instanceof Promise ? result.then(resolve, reject) : resolve(result);
});
} catch(e) {
reject(e);
}
} else if (self.status === REJECTED) {
try {
setTimeout(() => {
const result = onRejected(self.reason);
result instanceof Promise ? result.then(resolve, reject) : resolve(result);
})
} catch(e) {
reject(e);
}
}
});
}
catch(onRejected) {
return this.then(null, onRejected);
}
static resolve(value) {
if (value instanceof Promise) {
// 如果是Promise实例,直接返回
return value;
} else {
// 如果不是Promise实例,返回一个新的Promise对象,状态为FULFILLED
return new Promise((resolve, reject) => resolve(value));
}
}
static reject(reason) {
return new Promise((resolve, reject) => {
reject(reason);
})
}
static all(promiseArr) {
const len = promiseArr.length;
const values = new Array(len);
// 记录已经成功执行的promise个数
let count = 0;
return new Promise((resolve, reject) => {
for (let i = 0; i < len; i++) {
// Promise.resolve()处理,确保每一个都是promise实例
Promise.resolve(promiseArr[i]).then(
val => {
values[i] = val;
count++;
// 如果全部执行完,返回promise的状态就可以改变了
if (count === len) resolve(values);
},
err => reject(err),
);
}
})
}
static race(promiseArr) {
return new Promise((resolve, reject) => {
promiseArr.forEach(p => {
Promise.resolve(p).then(
val => resolve(val),
err => reject(err),
)
})
})
}
}
案例二:
3.2 从源码出发,从案例出发来回答问题:
源码出发:
简述: 只要是问及Promise相关函数、call、bind、apply、防抖和节流、八大排序都要能够熟悉它的代码,有小伙伴会说,能把这些都搞定,也太难了把,不用担心,下一章,小编会告诉你怎么准备代码这一块的内容
为什么要从代码出发: 因为对于大部分面试官来说,你能真正的理解源码实现,你是真的明白了它的原理,而不是cv前端程序员,在他眼中,你会让他眼前一亮,因为你是一个基础牢的有心之人,那这样的人,不就是人们都喜欢的人嘛(这一点真的很重要奥,也是区分你和别的面试者的水平的重要之处)
从案例出发:
简述: 在回答一些前端问题的时候,要多从问题的应用场景示例出发。例如Promise原理,你可以在最后阐述你怎么用Promise来封装request.js,或者在哪一个功能实现下你用到了Promise中的all、race等方法
为什么从案例出发: 每个人都在简历上写了解或熟悉JavaScript,但是对于面试官来说,他并不能知道你对JS的认知水平是如何的,但是你能够从案例场景出发,就真实的告诉了面试官,这个特性、这个函数我有真实的用过,主要体现一个真实性
3.3 小编的八股文笔记
当时为了准备各大互联网的各种面试,小编每次都会认真整理每一个重要的八股文问题,手敲了超10万字的笔记,目前仍然珍藏在自己的电脑里,有需要的小伙伴文末有领取方式,如果小伙伴需要前端的面试思路辅导,可以评论区私信我,我把我的经验告诉你,让你少走弯路
我的笔记是结合了自己对问题的理解,从我的三步走策略整理出来的面试题,题目也是我面试几十轮下来觉得一些很重要的问题,同时一些重要的原理问题都附上了demo源码


我们都知道,参加笔试、行测、等待面试都是让人痛苦的3.4 坚持的力量
但痛苦的日子,需要坚持,要相信,坚持的力量,那些过程中的辛苦总会在春暖花开的日子烟消云散
每一次的面试,都当成是八股文的再次记忆,因为有压力,你的记忆力会有质的提高,多轮下来哪些八股文你会忘不掉
如何准备笔试题?
当时整理的各种代码题类型
1、口诀记忆法
下图是我当时准备的八股文中的场景代码题,这么多题目,纯靠记忆记住每一个代码是非常困难的,那么小编整理一个口诀记忆法
以防抖节流为例:
口诀: 一防抖,二节流。防抖棒、节流桑
解释:
一防抖:防抖实现有一种写法
二节流:节流有两种写法
防抖棒:防抖的名字是debounce(谐音棒)
节流桑:节流的名字是throtte(谐音是桑,老家的方言哈)
引申: 那么所有的代码题,都可以根据自己的思路,将一些关键代码整理成口诀,浓缩代码,只要记住关键语句,你就可以复述出完整的代码。
2、过程思路梳理
以bind、call方法为例,来用简短的话术总结核心步骤,来帮助自己用更少的话就可以记住代码的写法,如下图所示
领取方式:————》点击此处领取《————