2023-07-17 前端面试题
- 写一个方法,比如有一个刷新 token 的接口请求,还有其他的请求,当刷新 token 的请求正在进行的时候,同时其他请求也发出来的时候,要等待 token 接口请求返回后再继续其他的请求?
- 分别使用 Proxy、Object.definePropty 实现对一个对象进行劫持打印出读取和赋值的值?
- 简单说一下 JavaScript 的继承、原型和原型链?
- 你觉得做得最好的项目是哪一个?(简历)
- Hr 或者老大会问:说 3 个你的优势?
- 项目亮点难点,如何提炼亮点难点? (粉丝提问)
1. 写一个方法,比如有一个刷新 token 的接口请求,还有其他的请求,当刷新 token 的请求正在进行的时候,同时其他请求也发出来的时候,要等待 token 接口请求返回后再继续其他的请求?
let refreshTokenPromise = null; // 用来存放刷新token的请求Promise
async function refreshToken() {
// 这里模拟实现刷新token的操作
return new Promise((resolve) =>
setTimeout(() => {
resolve("new_token");
}, 1000)
);
}
// 添加请求拦截器
axios.interceptors.request.use(
async function (config) {
// 在发送请求之前做些什么
const userStore = store.state.user; // 从store中获取用户token和过期时间
let userToken = userStore.userToken; // 用户token
const expire = userStore.expire; // 过期时间 时间戳
// 如果token 不存在或者token 过期
if (!userToken || Date.now() > expire) {
if (!refreshTokenPromise) {
// 如果还没有开始刷新token,则开始刷新,并保存Promise对象
refreshTokenPromise = refreshToken().finally(() => {
refreshTokenPromise = null; // 无论刷新成功还是失败,确保清除refreshTokenPromise
});
}
userToken = await refreshTokenPromise; // 等待刷新操作完成
store.state.user.userToken = userToken; // 更新store中的用户token
}
config.headers["Authorization"] = `Bearer ${userToken}`; // 使用新的token
return config;
},
function (error) {
// 对请求错误做些什么
return Promise.reject(error);
}
);
// 添加响应拦截器
axios.interceptors.response.use(
function (response) {
// 2xx 范围内的状态码都会触发该函数。
// 对响应数据做点什么
return response;
},
function (error) {
// 超出 2xx 范围的状态码都会触发该函数。
// 对响应错误做点什么
return Promise.reject(error);
}
);
2. 分别使用 Proxy、Object.defineProperty 实现对一个对象进行劫持打印出读取和赋值的值?
// 使用 Proxy
let obj = { name: "Tom", age: 20 };
let handler = {
get(target, key) {
console.log(`读取${key}: ${target[key]}`);
return Reflect.get(target, key);
},
set(target, key, value) {
console.log(`设置${key}为: ${value}`);
return Reflect.set(target, key, value);
},
};
let proxyObj = new Proxy(obj, handler);
proxyObj.name; // 读取name: Tom
proxyObj.age = 21; // 设置age为: 21
// 使用 Object.defineProperty
let obj2 = { name: "Jerry", age: 18 };
function defineReactive(obj, key, val) {
Object.defineProperty(obj, key, {
get() {
console.log(`读取${key}: ${val}`);
return val;
},
set(newVal) {
console.log(`设置${key}为: ${newVal}`);
val = newVal;
},
});
}
for (let key in obj2) {
defineReactive(obj2, key, obj2[key]);
}
obj2.name; // 读取name: Jerry
obj2.age = 19; // 设置age为: 19
希望是一个复杂的对象,要考虑到对象还嵌套这对象
如果需要处理嵌套对象,那我们就需要对我们的劫持逻辑进行改进,使其可以递归处理对象。以下是一个改进的版本:
// 使用 Proxy
function deepProxy(obj) {
let handler = {
get(target, key) {
console.log(`读取${key}: ${target[key]}`);
if (typeof target[key] === "object" && target[key] !== null) {
return new Proxy(target[key], handler);
} else {
return Reflect.get(target, key);
}
},
set(target, key, value) {
console.log(`设置${key}为: ${value}`);
return Reflect.set(target, key, value);
},
};
return new Proxy(obj, handler);
}
let obj = { name: "Tom", age: 20, info: { hobby: "reading" } };
let proxyObj = deepProxy(obj);
proxyObj.info.hobby; // 读取hobby: reading
proxyObj.info.hobby = "gaming"; // 设置hobby为: gaming
// 使用 Object.defineProperty
function defineReactive(obj) {
Object.keys(obj).forEach((key) => {
let internalValue = obj[key];
if (typeof internalValue === "object" && internalValue !== null) {
defineReactive(internalValue);
}
Object.defineProperty(obj, key, {
get() {
console.log(`读取${key}: ${internalValue}`);
return internalValue;
},
set(newVal) {
console.log(`设置${key}为: ${newVal}`);
if (typeof newVal === "object" && newVal !== null) {
defineReactive(newVal);
}
internalValue = newVal;
},
});
});
}
let obj2 = { name: "Jerry", age: 18, info: { hobby: "drawing" } };
defineReactive(obj2);
obj2.info.hobby; // 读取hobby: drawing
obj2.info.hobby = "singing"; // 设置hobby为: singing
3. 简单说一下 JavaScript 的继承、原型和原型链?
我们先简单解释一下 JavaScript 中的继承、原型和原型链,然后通过具体的例子来展示。
继承(Inheritance):在 JavaScript 中,对象可以继承另一个对象的属性和方法。这种继承关系并不是通过复制来实现的,而是通过原型链来实现的。
原型(Prototype):在 JavaScript 中,每一个对象都有一个指向其原型对象的内部链接。原型对象也有自己的原型,这样层层向上直到一个对象的原型为 null。根据定义,null 没有原型,并作为这个原型链中的最后一个环节。
原型链(Prototype Chain):当试图访问一个对象的属性时,如果对象自身没有这个属性,那么 JavaScript 会沿着原型链向上查找,如果所有的原型对象都没有找到,则返回 undefined。
下面我们来看一个具体的例子:
// 创建一个构造函数
function Person(firstName, lastName) {
this.firstName = firstName;
this.lastName = lastName;
}
// 在 Person 的原型对象上添加方法
Person.prototype.getFullName = function () {
return this.firstName + " " + this.lastName;
};
// 创建一个新的 Person 对象
var john = new Person("John", "Doe");
// 这个对象由 Person 构造函数创建,因此它的原型是 Person.prototype
// 当我们调用 john 的 getFullName 方法时,JavaScript 首先检查 john 对象是否有这个方法
// 如果没有,它会沿着原型链向上查找,这里它在 Person.prototype 中找到了这个方法
console.log(john.getFullName()); // 输出 "John Doe"
// 如果我们尝试访问一个不存在的属性,比如 john.age,JavaScript 会沿着原型链向上查找
// 如果所有的原型对象都没有找到,则返回 undefined
console.log(john.age); // 输出 undefined
这就是 JavaScript 中的继承、原型和原型链的基本概念和示例。在 JavaScript 中,理解这些概念是非常重要的,因为它们是 JavaScript 对象和继承的基础。
4. 你觉得做得最好的项目是哪一个?
这个问题的答案因人而异,取决于你的个人经验和项目。你应该提到你认为最具挑战性、最有创新性或者对你职业生涯影响最大的项目。具体来说,你可以描述项目的目标、你的角色、你的贡献以及项目的结果。
5. Hr 或者老大会问:说 3 个你的优势?
这个问题的答案也因人而异,要根据自己的经验和能力来回答。你可能会提到你的专业技能、沟通能力、解决问题的能力、团队合作的能力等等。
6. 项目亮点难点,如何提炼亮点难点?
在描述项目的亮点和难点时,重要的是要讲故事,让听者了解你是如何克服挑战、取得成功的。亮点可能是创新的解决方案,优秀的团队合作,或者项目对业务产生了重大影响。难点可能是技术上的挑战,或者项目管理上的问题。在描述难点时,应该强调你是如何解决问题的,而不仅仅是问题本身。