欢迎光临
我们一直在努力

高频手写题集锦

1、函数工具类  

  •  防抖:限制函数执行频率,适用于搜索框输入、窗口 resize 等场景。-》“多次触发,只执行最后一次”
  • 节流:固定时间间隔执行,适用于滚动加载。-》“一定间隔时间内,只执行一次”
  • bind/call/apply:理解 this 指向和参数处理,bind 需支持 new 调用。
  • 函数柯里化:将多参数函数转换为一系列接受单个参数的函数

1.1、防抖

const debounce = (func,wait=50) => {
let timer = null;
return function (…args) {
if(timer)clearTimeout(timer);
timer = setTimeout(()=>{
func.apply(this,args)
},wait)
}
}

1.2、节流

const throttle = (func,wait = 50) => {
let lastTime = 0;
return function (…args) {
let now = + new Date();
if(now – lastTime > wait) {
lastTime=now;
func.apply(this,args);
}
}
}

1.3、bind/call/apply

bind/call/apply:用于显示绑定函数this的方法;

特性applycallbind
调用时机 立即调用 返回一个新函数
参数传递 数组或类数组 逐个列出
是否立即执行
返回值 函数的执行结果 绑定后的新函数

Function.prototype.myCall = function(context,…args) {
// 如果 context 是 null 或 undefined,在非严格模式下指向全局对象
if(context === null || context === undefined) {
context = typeof globalThis !=='undefined' ? globalThis : window;
}

// 确保 context 是一个对象
//基本类型值(如数字、字符串等)会被转换为对应的包装对象(如 Number, String),这样才能在上面挂载临时属性。
context = Object(context);

// 使用Symbol 避免属性名冲突
const fnKey = Symbol("fn");

// 将当前函数作为context的一个方法
// 这里的this 指向调用myCall的函数本身(例如 func.myCall(…) 中的func)
context[fnKey] = this;

// 执行函数并获取结果
const result = context[fnKey](…args);

// 删除临时属性
delete context[fnKey];

return result;
}

Function.prototype.myApply = function(context, argsArray) {
// 处理 context 为 null/undefined 的情况
if (context === null || context === undefined) {
context = typeof globalThis !== 'undefined' ? globalThis : window;
}
context = Object(context);

const fnKey = Symbol('fn');
context[fnKey] = this;

// 确保 argsArray 是数组或类数组,如果没有传参则使用空数组
let result;
if (argsArray&&Array.isArray(Array.from(argsArray))) {
result = context[fnKey](…argsArray);
} else {
result = context[fnKey]();
}

delete context[fnKey];
return result;
};

Function.prototype.myBind = function(context, …bindArgs) {
const originalFunc = this;

// 返回绑定函数
function boundFunction(…callArgs) {
// 合并预置参数和调用时传入的参数
const args = bindArgs.concat(callArgs);

// 关键:如果 boundFunction 是通过 new 调用的,则 this 指向新实例,否则为绑定的 context
// new.target 检测是否为构造函数调用
const isConstructorCall = typeof new.target !== 'undefined';
const thisArg = isConstructorCall ? this : context;

return originalFunc.apply(thisArg, args);
}

// 维护原型链:如果原函数有 prototype,则绑定函数应继承它,以便 new 绑定函数时能正确继承原函数的原型
// 使用空函数中转,避免直接修改 boundFunction.prototype 影响原函数原型
if (originalFunc.prototype) {
const EmptyCtor = function() {};
EmptyCtor.prototype = originalFunc.prototype;
boundFunction.prototype = new EmptyCtor();
}

return boundFunction;
};

1.4、函数柯里化

/**
* 将多参数函数转换为柯里化函数
* @param {Function} fn 需要柯里化的原函数
* @param {number} [arity=fn.length] 可选:指定参数个数,默认使用 fn.length
* @returns {Function} 柯里化后的函数
**/
function curry(fn,arity = fn.length) {
// 返回一个新函数,由于收集参数
return function curried(…args) {
// 如果收集到的参数数量 >= 所需的参数个数,直接执行原函数
if(args.length>= arity){
return fn.apply(this,args);
}

// 否则返回一个新函数,继续收集剩余参数
return function (…nextArgs) {
return curried.apply(this,args.concat(nextArgs));
}
}
}

// 示例
function sum(a,b,c) {
return a+b+c;
}

const curriedSum = curry(sum);
curriedSum(1)(2)(3); // 6
curriedSum(1,2)(3); // 6
curriedSum(1)(2,3); // 6
curriedSum(1,2,3); // 6

2、异步编程类

2.1、Promise 实现

PromiseA+规范,非ES6的Promise

核心:构造函数 new Promise / then

const STATE = {PENDING:'pending',FULFILLED:'fulfilled',REJECTED:'rejected'}

class MyPromise {

#state = STATE.PENDING; // 私有属性
#result = undefined;
#handlers = [];

constructor(executor){
const resolve=(data)=>{
this.#changeState(STATE.REJECTED,data)
};
const reject=(reason)=>{
this.#changeState(STATE.FULFILLED,reason)
}

// 只能捕获同步错误
try {
executor(resolve,reject)
}catch(err){
reject(err)
}
}

// 私有方法:修改状态
#changeState(state,result){
if(this.#state !== STATE.PENDING)return;
this.#state = state;
this.#result = result;
this.#run();
}

// 将函数放入微队列
#runMicroTask(func){
// Node环境
if(typeof process === 'object' && typeof process.nextTick === 'function'){
process.nextTick(func);
}else if(typeof MutationObserver==='function'){
// 浏览器环境
const ob = new MutationObserver(func);
const textNode = document.createTextNode('1');
ob.observe(textNode,{characterData:true});
textNode.data='2';
}else{
setTimeout(func,0)
}
}

// 判断是否是Promise 满足Promise A+规范
#isPromiseLike(value){
if(value!==null&&(typeof value ==='object'||typeof value==='function'){
return typeof value.then === 'function';
}
return false;
}

#runOne(callback,resolve,reject){
this.#runMicroTask(()=>{
if(typeof callback !=='function'){
const settled = this.#state === STATE.FULFILLED?resolve:reject;
settled(this.#result)
return;
}
try{
const data = callback(this.#result);
if(this.#isPromiseLike(data)){
data.then(resolve,reject)
}else{
resolve(data)
}
resolve(data)
}catch(err){
reject(err)
}
})
}

#run(){
if(this.#state === STATE.PENDING)return;
while(this.#handlers.length){
const {onFulfilled,onRejected,resolve,reject} = this.#handlers.shift()
if(this.#state === STATE.FULFILLED){
this.#runOne(onFulfilled,resolve,reject)
}else(this.#state === STATE.REJECTED){
this.#runOne(onRejected,resolve,reject)
}
}
}

then(onFulfilled,onRejected){
return new MyPromise((resolve,reject)=>{
this.#handlers.push({
onFulfilled,onRejected,resolve,reject
})
this.#run();
})
}
}

2.2、async/await 

async/await本质:async函数返回一个Promise,函数内的 await 会暂停执行,等待右侧的 Promise 决议后回复执行并返回结果。

ES6的 Generator 函数  + 自动执行器   ——> 可以模拟这个行为

/**
* 模拟 async/await 的自动执行器
* @param {GeneratorFunction} generatorFunc 一个 Generator 函数
* @returns {Function} 返回一个具有 async 行为的函数(执行后返回 Promise)
*/
function asyncToGenerator(generatorFunc) {
return function(…args) {
// 1. 调用 generator 函数,得到迭代器对象
const gen = generatorFunc.apply(this, args);

// 2. 返回一个 Promise,因为 async 函数最终返回 Promise
return new Promise((resolve, reject) => {
// 3. 定义递归函数,用于驱动 generator 执行
function step(key, arg) {
let result;
try {
// 执行 gen.next() 或 gen.throw()
result = gen[key](arg);
} catch (err) {
// 如果执行过程中抛出异常,直接 reject 这个 Promise
return reject(err);
}

const { value, done } = result;

if (done) {
// 如果 generator 执行完毕,resolve 最终结果
return resolve(value);
} else {
// 4. 保证 yield 后面的表达式被包装为 Promise
return Promise.resolve(value).then(
(res) => step('next', res),
(err) => step('throw', err)
);
}
}

// 从第一次 next() 开始执行
step('next');
});
};
}

// 模拟一个异步任务(例如网络请求)
function fetchData(id) {
return new Promise((resolve) => {
setTimeout(() => {
resolve(`用户数据 ${id}`);
}, 1000);
});
}

// 使用 Generator 函数模拟 async 函数
const getUserData = asyncToGenerator(function* (id) {
console.log('开始获取数据…');
const data1 = yield fetchData(id);
console.log('第一次拿到数据:', data1);

const data2 = yield fetchData(data1.length);
console.log('第二次拿到数据:', data2);

return [data1, data2];
});

// 调用模拟的 async 函数,它会返回 Promise
getUserData(123).then((result) => {
console.log('最终结果:', result);
}).catch((err) => {
console.error('出错:', err);
});

2.3、EventBus事件总线

/**
* 事件总线 EventBus
* 提供事件的订阅、发布、取消订阅及一次性监听能力
*/
class EventBus {
constructor() {
// 存储事件名称与对应的回调集合
this.events = new Map(); // key: string|symbol, value: Set<Function>
}

/**
* 订阅事件
* @param {string|symbol} event – 事件名称
* @param {Function} callback – 回调函数
* @returns {Function} 取消订阅函数
*/
on(event, callback) {
if (!this.events.has(event)) {
this.events.set(event, new Set());
}
this.events.get(event).add(callback);

// 返回取消订阅函数,方便用户主动移除
return () => this.off(event, callback);
}

/**
* 一次性订阅:触发一次后自动移除
* @param {string|symbol} event – 事件名称
* @param {Function} callback – 回调函数
* @returns {Function} 取消订阅函数
*/
once(event, callback) {
// 包装回调,执行后立即移除自身
const wrapper = (…args) => {
callback(…args);
this.off(event, wrapper);
};
// 将包装后的回调注册到事件中
this.on(event, wrapper);
// 返回取消订阅函数(实际取消的是 wrapper)
return () => this.off(event, wrapper);
}

/**
* 取消订阅
* @param {string|symbol} event – 事件名称
* @param {Function} [callback] – 可选,指定要移除的回调;不传则移除该事件下所有回调
*/
off(event, callback) {
if (!this.events.has(event)) return;
const callbacks = this.events.get(event);

if (callback) {
callbacks.delete(callback);
if (callbacks.size === 0) {
this.events.delete(event);
}
} else {
// 移除整个事件
this.events.delete(event);
}
}

/**
* 触发事件
* @param {string|symbol} event – 事件名称
* @param {…any} args – 传递给回调的参数
*/
emit(event, …args) {
if (!this.events.has(event)) return;
// 取回调集合的快照,避免遍历过程中回调新增或删除导致异常
const callbacks = […this.events.get(event)];
callbacks.forEach(callback => {
try {
callback(…args);
} catch (error) {
// 单个回调错误不影响其他回调执行,可根据需要自定义错误处理
console.error(`[EventBus] Error in callback for "${String(event)}":`, error);
}
});
}

/**
* 清空所有事件监听(可选方法,用于重置)
*/
clear() {
this.events.clear();
}
}

module.exports = EventBus; // Node 导出,浏览器中可改为 window.EventBus = EventBus

3、对象与原型类

3.1、new操作符

new操作符执行以下步骤:

  • 创建一个新的空对象。

  • 将该对象的原型设置为构造函数的prototype属性(即newObj.__proto__ = Constructor.prototype)。

  • 将构造函数的this绑定到新对象,并执行构造函数(传递参数)。

  • 如果构造函数返回一个对象,则返回该对象;否则返回创建的新对象。

/**
* 模拟 new 操作符的行为
* @param {Function} Constructor 构造函数
* @param {…any} args 构造函数的参数
* @returns {object} 构造函数的实例
*/
function myNew(Constructor, …args) {
// 1. 创建一个新对象,并将其原型指向构造函数的 prototype 属性
const obj = Object.create(Constructor.prototype);

// 2. 执行构造函数,将 this 绑定到新对象,并传入参数
const result = Constructor.apply(obj, args);

// 3. 判断构造函数是否返回了一个对象(或函数),若是则返回该对象,否则返回新创建的对象
if (result !== null && (typeof result === 'object' || typeof result === 'function')) {
return result;
}
return obj;
}

3.2、instanceof

function myInstanceof(obj, constructor) {
// 处理基本类型(null 或非对象)
if (obj === null || (typeof obj !== 'object' && typeof obj !== 'function')) {
return false;
}

// 获取构造函数的 prototype
const prototype = constructor.prototype;
if (prototype === undefined) {
throw new TypeError('Right-hand side of instanceof is not callable');
}

// 获取对象的原型
let proto = Object.getPrototypeOf(obj);
while (proto !== null) {
if (proto === prototype) {
return true;
}
proto = Object.getPrototypeOf(proto);
}
return false;
}

3.3、Object.create

Object.create 创建一个新对象,使用现有的对象作为新创建对象的原型(prototype)

function createObject(proto) {
// 1. 参数校验:proto 必须是对象或 null,否则抛出 TypeError
if (proto !== null && typeof proto !== 'object') {
throw new TypeError('Object prototype may only be an Object or null');
}

// 2. 处理 proto === null:创建一个没有任何原型的对象
if (proto === null) {
// 方法一:使用 Object.create(null) 直接实现(简洁)
// return Object.create(null);

// 方法二:手动实现,利用 Object.setPrototypeOf
const obj = {};
Object.setPrototypeOf(obj, null);
return obj;
}

// 3. 常规情况:通过临时构造函数连接原型链
function F() {}
F.prototype = proto;
return new F();
}

3.4、深拷贝 deepClone

function deepClone(value, hash = new WeakMap()) {
// 基本类型 + null + undefined + function(函数直接返回引用,因为一般不需要深拷贝)
if (value === null || typeof value !== 'object') {
return value;
}

// 处理 Date
if (value instanceof Date) {
return new Date(value);
}

// 处理 RegExp
if (value instanceof RegExp) {
return new RegExp(value.source, value.flags);
}

// 处理 Map
if (value instanceof Map) {
const cloneMap = new Map();
hash.set(value, cloneMap);
for (let [k, v] of value) {
cloneMap.set(deepClone(k, hash), deepClone(v, hash));
}
return cloneMap;
}

// 处理 Set
if (value instanceof Set) {
const cloneSet = new Set();
hash.set(value, cloneSet);
for (let item of value) {
cloneSet.add(deepClone(item, hash));
}
return cloneSet;
}

// 处理数组和普通对象(包括其他内置构造器如 Error,简单处理为创建新实例)
// 获取原型,保持继承关系
const proto = Object.getPrototypeOf(value);
const clone = Array.isArray(value) ? [] : Object.create(proto);

// 防止循环引用
if (hash.has(value)) {
return hash.get(value);
}
hash.set(value, clone);

// 拷贝所有自有属性(包括不可枚举和 Symbol 属性)
const allKeys = Reflect.ownKeys(value);
for (let key of allKeys) {
const desc = Object.getOwnPropertyDescriptor(value, key);
if (desc && (desc.get || desc.set)) {
// 访问器属性直接复制描述符,不进行深拷贝
Object.defineProperty(clone, key, desc);
} else {
// 数据属性递归深拷贝值
clone[key] = deepClone(value[key], hash);
}
}

return clone;
}

4、数组方法类

4.1、filter

Array.prototype.myFilter = function(callback, thisArg) {
// 1. 检查 this 是否为 null 或 undefined
if (this == null) {
throw new TypeError('Array.prototype.filter called on null or undefined');
}
// 2. 确保 callback 是一个函数
if (typeof callback !== 'function') {
throw new TypeError(callback + ' is not a function');
}

// 3. 将 this 转换为对象(包装对象)
const O = Object(this);
// 4. 获取长度,使用无符号右移确保是正整数
const len = O.length >>> 0;

// 5. 创建结果数组
const result = [];

// 6. 遍历数组索引
for (let i = 0; i < len; i++) {
// 检查索引 i 是否存在于原对象中(处理稀疏数组)
if (i in O) {
// 获取当前元素
const element = O[i];
// 调用回调,传入三个参数:当前值、索引、原对象
if (callback.call(thisArg, element, i, O)) {
// 如果回调返回真值,则将元素压入结果数组
result.push(element);
}
}
}

// 7. 返回新数组
return result;
};

4.2、find

Array.prototype.myFind = function(callback, thisArg) {
// 1. 检查 this 是否为 null 或 undefined
if (this == null) {
throw new TypeError('Array.prototype.find called on null or undefined');
}
// 2. 确保 callback 是一个函数
if (typeof callback !== 'function') {
throw new TypeError(callback + ' is not a function');
}

// 3. 将 this 转换为对象(包装对象)
const O = Object(this);
// 4. 获取长度,使用无符号右移确保是正整数
const len = O.length >>> 0;

// 5. 遍历数组索引
for (let i = 0; i < len; i++) {
// 检查索引 i 是否存在于原对象中(处理稀疏数组,空槽位会被跳过)
if (i in O) {
const element = O[i];
// 调用回调,传入三个参数:当前值、索引、原对象
if (callback.call(thisArg, element, i, O)) {
// 找到第一个满足条件的元素,立即返回该元素值
return element;
}
}
}

// 6. 未找到任何满足条件的元素,返回 undefined
return undefined;
};

4.3、map

Array.prototype.myMap = function(callback, thisArg) {
// 1. 检查 this 是否为 null 或 undefined
if (this == null) {
throw new TypeError('Array.prototype.map called on null or undefined');
}
// 2. 确保 callback 是一个函数
if (typeof callback !== 'function') {
throw new TypeError(callback + ' is not a function');
}

// 3. 将 this 转换为对象(包装对象)
const O = Object(this);
// 4. 获取长度,使用无符号右移确保是正整数
const len = O.length >>> 0;

// 5. 创建与原数组长度相同的新数组(保留空槽位)
const result = new Array(len);

// 6. 遍历数组索引
for (let i = 0; i < len; i++) {
// 检查索引 i 是否存在于原对象中(处理稀疏数组)
if (i in O) {
const element = O[i];
// 调用回调,传入三个参数:当前值、索引、原对象
const mappedValue = callback.call(thisArg, element, i, O);
// 将结果赋值给新数组的对应索引
result[i] = mappedValue;
}
// 如果原数组该索引不存在(空槽),则 result[i] 保持为空槽(不赋值)
}

// 7. 返回新数组
return result;
};

4.4、reduce

Array.prototype.myReduce = function(callback, initialValue) {
// 1. 检查 this 是否为 null 或 undefined
if (this == null) {
throw new TypeError('Array.prototype.reduce called on null or undefined');
}
// 2. 确保 callback 是一个函数
if (typeof callback !== 'function') {
throw new TypeError(callback + ' is not a function');
}

// 3. 将 this 转换为对象并获取长度
const O = Object(this);
const len = O.length >>> 0;

// 4. 判断是否提供了初始值
let accumulator;
let startIndex = 0;

if (arguments.length >= 2) {
// 提供了初始值
accumulator = initialValue;
} else {
// 没有提供初始值,需要寻找第一个有效的元素作为初始累积器
let found = false;
for (let i = 0; i < len; i++) {
if (i in O) {
accumulator = O[i];
startIndex = i + 1;
found = true;
break;
}
}
// 如果数组为空或全是空槽,且没有初始值,抛出类型错误
if (!found) {
throw new TypeError('Reduce of empty array with no initial value');
}
}

// 5. 遍历剩余元素
for (let i = startIndex; i < len; i++) {
// 仅处理存在的索引(跳过空槽位)
if (i in O) {
accumulator = callback.call(undefined, accumulator, O[i], i, O);
}
}

// 6. 返回累积结果
return accumulator;
};

4.5、数组去重

/**
* 数组去重(基于 Map,保持原顺序)
* @param {Array} arr – 需要去重的数组
* @returns {Array} 去重后的新数组
*/
function uniqueArray(arr) {
// 使用 Map 记录已出现的元素
const seen = new Map();
return arr.filter(item => {
// Map 中 key 使用元素本身,可正确区分 NaN 和对象引用
if (!seen.has(item)) {
seen.set(item, true);
return true;
}
return false;
});
}

4.6、数组扁平化

// 方案1:递归+reduce(最简洁)
function flatten(arr) {
return arr.reduce((acc, val) =>
acc.concat(Array.isArray(val) ? flatten(val) : val),
[]);
}

方案2:支持深度控制的版本(模拟元素flat)——》推荐
function flatten(arr, depth = Infinity) {
if (depth === 0) return arr.slice(); // 浅拷贝一份
return arr.reduce((acc, val) => {
if (Array.isArray(val) && depth > 0) {
// 深度减1后递归
acc.push(…flatten(val, depth – 1));
} else {
acc.push(val);
}
return acc;
}, []);
}

方案3:迭代+栈(避免递归栈溢出)
function flatten(arr) {
const stack = […arr];
const result = [];
while (stack.length) {
const next = stack.pop();
if (Array.isArray(next)) {
stack.push(…next); // 展开后重新压入栈
} else {
result.unshift(next); // 逆序插入保持原顺序
}
}
return result;
}

方案4:使用生成器函数(惰性求值)
function* flattenGenerator(arr) {
for (const item of arr) {
if (Array.isArray(item)) {
yield* flattenGenerator(item);
} else {
yield item;
}
}
}

function flatten(arr) {
return […flattenGenerator(arr)];
}

赞(0)
未经允许不得转载:171主机测评 » 高频手写题集锦
分享到: 更多 (0)

评论 抢沙发

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址