面试常考的高频手写JS题汇总
2025.09.19 12:47浏览量:0简介:掌握高频手写JS题是面试成功的关键,本文汇总了类型检查、数组操作、异步处理等核心题型,并提供解题思路与代码示例。
面试常考的高频手写JS题汇总
在前端开发岗位的面试中,手写JavaScript代码题是考察候选人基础能力的重要环节。这类题目不仅检验对语言特性的理解,更能体现问题拆解与逻辑实现能力。本文将系统梳理面试中高频出现的手写JS题型,覆盖类型检查、数组操作、异步处理等核心场景,并提供解题思路与代码示例。
一、类型检查与转换
1. 深度类型判断
基础typeof
和instanceof
无法满足复杂场景需求,需实现能区分null
、Array
、Date
等特殊类型的函数:
function deepType(obj) {
return Object.prototype.toString.call(obj).slice(8, -1).toLowerCase();
}
// 测试用例
deepType([]); // "array"
deepType(new Date()); // "date"
deepType(null); // "null"
该实现利用Object.prototype.toString
的内部行为,通过截取字符串获取精确类型标识,比手动判断constructor
更可靠。
2. 安全类型转换
实现将任意值转为指定类型的函数,需处理边界情况:
function safeConvert(value, targetType) {
switch (targetType.toLowerCase()) {
case 'number':
return isNaN(Number(value)) ? 0 : Number(value);
case 'string':
return String(value);
case 'boolean':
return value === '' ? false : !!value;
default:
throw new Error('Unsupported type');
}
}
// 测试用例
safeConvert('123', 'number'); // 123
safeConvert(null, 'string'); // "null"
关键点在于处理null
、空字符串等特殊值,避免隐式转换的意外行为。
二、数组操作算法
1. 扁平化数组
实现多级嵌套数组的扁平化,支持指定深度:
function flatten(arr, depth = Infinity) {
return depth > 0
? arr.reduce((acc, val) =>
Array.isArray(val)
? acc.concat(flatten(val, depth - 1))
: acc.concat(val),
[])
: arr.slice();
}
// 测试用例
flatten([1, [2, [3]]], 1); // [1, 2, [3]]
递归实现需注意深度控制与性能优化,对于超大数组可改用迭代方式。
2. 数组去重
实现高性能去重函数,考虑不同数据类型:
function unique(arr) {
const seen = new WeakSet(); // 仅对对象有效
const result = [];
const stringSet = new Set();
for (const item of arr) {
const isObject = deepType(item) === 'object';
const key = isObject ? JSON.stringify(item) : item;
if (isObject
? !seen.has(item) && seen.add(item)
: !stringSet.has(key) && stringSet.add(key)) {
result.push(item);
}
}
return result;
}
// 测试用例
unique([1, 1, '1', {a:1}, {a:1}]); // [1, '1', {a:1}]
混合使用Set
和WeakSet
处理原始值与引用类型,JSON.stringify
需注意对象属性顺序问题。
三、异步编程实现
1. Promise链式调用
实现简化版Promise,支持then
方法:
class MyPromise {
constructor(executor) {
this.value = undefined;
this.reason = undefined;
this.onFulfilled = [];
this.onRejected = [];
const resolve = (value) => {
setTimeout(() => {
this.value = value;
this.onFulfilled.forEach(fn => fn(value));
}, 0);
};
executor(resolve, reject);
}
then(onFulfilled) {
return new MyPromise(resolve => {
this.onFulfilled.push(val => {
const result = onFulfilled(val);
if (result instanceof MyPromise) {
result.then(resolve);
} else {
resolve(result);
}
});
});
}
}
关键点在于异步调度与值穿透处理,实际实现需完善状态管理与错误处理。
2. 异步队列控制
实现并发数限制的异步任务队列:
class AsyncQueue {
constructor(concurrency = 1) {
this.concurrency = concurrency;
this.running = 0;
this.queue = [];
}
add(task) {
return new Promise((resolve, reject) => {
this.queue.push({ task, resolve, reject });
this.next();
});
}
next() {
while (this.running < this.concurrency && this.queue.length) {
const { task, resolve, reject } = this.queue.shift();
this.running++;
task().then(resolve, reject).finally(() => {
this.running--;
this.next();
});
}
}
}
// 使用示例
const queue = new AsyncQueue(2);
Array(5).fill().map((_,i) =>
queue.add(() => new Promise(r => setTimeout(() => {
console.log(i);
r(i);
}, 1000)))
);
通过维护运行计数器与任务队列实现并发控制,适用于批量API调用场景。
四、函数式编程实践
1. 柯里化实现
实现参数自动收集的柯里化函数:
function curry(fn) {
return function curried(...args) {
if (args.length >= fn.length) {
return fn.apply(this, args);
} else {
return function(...args2) {
return curried.apply(this, args.concat(args2));
}
}
};
}
// 测试用例
const sum = curry((a,b,c) => a+b+c);
sum(1)(2)(3); // 6
关键在于通过fn.length
判断参数收集完成时机,支持占位符的高级实现可进一步扩展。
2. 组合函数实现
实现函数组合工具,支持从右到左的执行顺序:
function compose(...fns) {
return function(initialValue) {
return fns.reduceRight((acc, fn) => fn(acc), initialValue);
};
}
// 测试用例
const add1 = x => x + 1;
const mul2 = x => x * 2;
compose(mul2, add1)(5); // 12
与pipe
函数的区别在于执行顺序,适用于数据处理流水线构建。
五、性能优化技巧
1. 防抖与节流
实现高频事件处理的防抖函数:
function debounce(fn, delay) {
let timer = null;
return function(...args) {
clearTimeout(timer);
timer = setTimeout(() => fn.apply(this, args), delay);
};
}
// 测试用例
window.addEventListener('resize', debounce(() => {
console.log('Resized');
}, 200));
节流函数的实现需使用时间戳或标记位控制执行频率,适用于滚动、输入等场景。
2. 记忆化缓存
实现函数结果缓存的装饰器:
function memoize(fn) {
const cache = new Map();
return function(...args) {
const key = JSON.stringify(args);
if (cache.has(key)) {
return cache.get(key);
}
const result = fn.apply(this, args);
cache.set(key, result);
return result;
};
}
// 测试用例
const expensiveCalc = memoize((a,b) => {
console.log('Calculating...');
return a * b;
});
expensiveCalc(2,3); // 执行计算
expensiveCalc(2,3); // 从缓存读取
适用于计算密集型函数,需注意缓存大小控制与参数序列化问题。
六、实战建议
- 分步拆解:将复杂问题拆解为输入处理、核心逻辑、输出返回三个阶段
- 边界测试:编写测试用例时覆盖null、undefined、空数组等边界情况
- 性能考量:对于大数据量处理,优先考虑时间复杂度与空间复杂度
- ES6+特性:合理使用
...
展开符、Proxy
、Generator
等现代语法简化实现
掌握这些高频手写题不仅能帮助通过面试,更能深化对JavaScript语言特性的理解。建议结合实际项目场景,理解每个实现背后的设计思想,而非机械记忆代码模板。
发表评论
登录后可评论,请前往 登录 或 注册