面试常考的高频手写JS题汇总
2025.09.19 12:47浏览量:4简介:掌握高频手写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'); // 123safeConvert(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语言特性的理解。建议结合实际项目场景,理解每个实现背后的设计思想,而非机械记忆代码模板。

发表评论
登录后可评论,请前往 登录 或 注册