1、手写EventBus
class EventBus { constructor() { // 构建map进行事件存储 this.map = new Map(); } on(name, fn) { // 事件订阅 const fnList = this.map.get(name); if (fnList) { fnList.push(fn); } else { this.map.set(name, [fn]); } } emit(name, args) { // 事件发布 const list = this.map.get(name); if (!list) { return -1 } list.forEach(fn => fn(args)); } off(name) { // 事件解除绑定 const flag = this.map.has(name); if (flag) { this.map.delete(name); } } once(name, fn) { // 只订阅一次 this.map.set(name, [fn]); }}
2、手写async函数
function asyncToGenerator(generator) { // 接收一个generator函数 return () => { // 返回一个函数,通过调用函数返回一个promise return new Promise((resolve, reject) => { const gen = generator(); // 让generator函数执行,返回一直迭代器 function step(key, val) { // 定义一个执行函数 let result; // 用一个值接收迭代器执行的结果 try { result = gen[key](val); // 执行迭代器函数,当key为next时候为正常执行 } catch(err) { // 当key不为next时,抛错,执行reject reject(err); } const [ done, value ] = result; // 将迭代器执行结果解构为done和value if (done) { // 当执行完成时,执行resolve resolve(value); } else { // 当执行未完成时 return Promise.resolve(value) // 将返回值用Promise.resolve包装,继续递归执行 .then(res => step('next', res)) .catch(err => step('err', err)); } } step('next'); // 执行自定义函数 }) }}
3、手写LRU
class LRU { constructor(max) { this.map = new Map(); // 存储相关队列 this.max = max; // 最大长度 } get(key) { // 获取值 const value = this.map.get(key); // 判断已经存在 if (!value) { // 如果不存在 return -1; } this.map.delete(key); // 如果存在先将存在的删除 this.map.set(key,value); // 将这个值存放到最后一位 return value; // 返回这个值 } set(key, value) { // 设置值 const val = this.map.get(key); // 是否存在 if (val) { // 如果存在,将这个存在的指删除 this.map.delete(key); } if (this.max >= this.map.size) { // 判断队列长度是否大于等于当前最大值 this.map.delete(this.map.keys().next().value); // 将这个队列的最前面的一个删除 } this.map.set(key, value); // 将这个值进行存储 }}
4、promise 最大并发
// 1.写法class PromisePool { constructor(max, fn) { this.max = max; // 最大并发数 this.fn = fn; // 执行的异步函数 this.pool = []; // 并发池 this.urls = []; // 执行的url队列 } start(urls) { // 开始执行最大并发函数 this.urls = urls; // 传入所有的ur数组 while(this.pool.length < this.max) { // 当并发池的数量小于最大数量 const url = this.urls.shift(); // 拿到第一个url数组 this.setTask(url); // 设置存储函数执行返回的promise } return this.run(Promise.race(this.pool)); // 利用Promise.race找到最开始结束的 } setTask(url) { // 存储函数执行的promise if (!url) return false; const task = this.fn(url); // 函数执行返回promise this.pool.push(task); // 放入执行队列中 task().then(() => { // 利用事件环的方式,执行完成进行并发池数据的删除 this.pool.splice(this.urls.indexOf(task), 1); // 删除并发池 }) } run(race) { // 执行Promise.race拿到第一个完成的任务 return race.then(() => { const url = this.urls.shift(); this.setTask(url); return this.run(Promise.race(this.pool)); }) }}// 2.写法class Scheduler { list = []; maxNum = 2; workingNum = 0; add(promiseCreator) { this.list.push(promiseCreator) } start() { for (let i = 0; i < this.maxNum; i++) { this.doNext(); } } doNext() { if (this.list.length && this.workingNum < this.maxNum) { this.workingNum ++; this.list.shift()().then(() => { this.workingNum --; this.doNext(); }) } }}const timeout = time => new Promise(resolve => setTimeout(resolve, time));const scheduler = new Scheduler();const addTask = (time, order) => { scheduler.add(() => timeout(time).then(() => console.log(order)));}addTask(1000, 1);addTask(2000, 2);scheduler.start();// 3.写法function asyncPool(poolLimit, array, iteratorFn) { let i = 0; const ret = []; // 存储所有的异步任务 const executing = []; // 存储正在执行的任务 const enqueue = function() { if (i === array.length) { return Promise.resolve(); } const item = array[i++]; const p = Promise.resolve().then(() => iteratorFn(item, array)); ret.push(p); let r = Promise.resolve(); if (poolLimit <= array.length) { const e = p.then(() => executing.splice(executing.indexOf(e), 1)); executing.push(e); if (executing.length >= poolLimit) { r = Promise.race(executing); } } return r.then(() => enqueue()); } return enqueue().then(() => Promise.all(ret));}const timeout = i => new Promise((resolve) => { setTimeout(() => { console.log(i); resolve(i) }, i)})asyncPool(2, [1000, 5000, 3000, 2000], timeout);
5、手写bind
/**1、bind方法可以绑定this指向2、bind方法返回一个绑定后的函数(高阶函数)3、如果绑定的函数被new了,当前函数的this指向当前的实例*/function myBind(context) { if (typeof this !== 'function') { // 如果调用不是一个函数就报错 throw new Error('bind must be called on a function'); } var _this = this; // 保存当前调用的this var args = Array.prototype.slice.call(arguments, 1); // 截取参数 var nop = function() {} function F() { return _this.apply(this instanceof nop ? this : context, [ ...arguments, ...args]); } nop.prototype = this.prototype; F.prototype = new nop(); return F;}
6、手写call、apply
function myCall(context) { if (typeof this !== 'function') { throw new TypeError('error'); } context = context || window; const args = [...arguments].slice(1); context.fn = this; const result = context.fn(...args); delete context.fn; return result;}function myApply(context) { if (typeof this !== 'function') { throw new TypeError('error'); } context = context || window; context.fn = this; let result if (arguments[1]) { result = context.fn(...args); } else { result = context.fn(); } delete context.fn; return result;}
7、手写promise.all和promise.allSettled
// Promise.all是必须等到所有成功之后才会成功,如果有一个失败则导致都失败Promise.all = function(promiseList) { // 传入一个数组 return new Promise((resolve, reject) => { // 返回一个promise const list = []; // 承载结果的数组 var i = 0; // 计数器 function judge(value, index) { // 判断是否全完的方法 list[index] = value; // 那个完成了,就对应放到数组的第几项 if (++i === promiseList.length) { // 如果计数器和传入数组相同,证明全部执行完成 resolve(list); // 返回结果 } } promiseList.forEach((promise, index) => { // 遍历数组 if (promise instanceof Promise) { // 如果是一个promise promise.then(res => { // 执行promise judge(res, index); // 正常执行 }).catch(err => { reject(err); // 失败 }) } else { // 如果不是promise就直接装入数组 judge(promise, index); } }) })}// Promise.allSettled是所有执行结果都会进行返回,装入一个数组中Promise.allSettled = function(promiseList) { let length = promiseList.length; // 记录有多个promise const result = []; // 承载promise执行结果 return new Promise((resolve, reject) => { // 返回一个promise for (let i = 0; i < promiseList.length; i++) { const current = promiseList[i]; current.then(res => { result.push({ status: 'fulfilled', value: res }); // 将成功的结果装入 }, err => { result.push({ status: 'rejected', reason: err }); // 将失败的结果装入 }).finally(() => { if (!--length) { // 当计数器为0时,就证明全部执行完毕 resolve(result); // 返回结果 } }) } })}
8、手写函数柯里化
// es5写法function curry(fn, args) { var fnLen = fn.length; var args = args || []; return function() { args = args.concat(Array.prototype.slice.call(arguments)); if (args.length === fn.length) { return curry.call(this, fn, args); } else { return fn.apply(this, args); } }}// es6 写法const curry = (fn, arr = []) => args => (arg => (arg.length === fn.length) ? fn(...arg) : curry(fn, arg)) ([ ...arr, ...args]);
9、手写instanceof
Function.prototype.myInstanceOf = function(left, right) { var proto = left.__proto__; // 获取实例的隐式原型 var prototype = right.prototype; // 获取构造函数的显式原型 while(true) { if (proto === null) { // 如果隐式原型为null,就返回错误 return false; } if (proto === prototype) { // 如果隐式原型和显式原型相同,返回正确 return true; } proto = proto.__proto__; // 一直递归找隐式原型的隐式原型 }}
10、手写new和Object.create
// Object.createfunction create(obj) { function F() {}; F.prototype = obj; return new F();}// new 的实现function new() { const obj = {}; const Con = [ ...arguments ].shift(1); obj.__proto__ = Con.prototype; const result = Con.apply(obj, [ ...arguments ]); return result instanceof Object ? result : obj;}
11、手写截流函数
function throttle(fn, delay) { let timer = null; let startTime = Date.now(); return function() { const currentTime = Date.now(); const remaining = delay - (currentTime - startTime); const context = this; const args = Array.prototype.slice.call(arguments); clearTimeout(timer); if (remaining < 0) { fn.apply(context, args); startTime = Date.now(); } else { timer = setTimeout() => fn.apply(context, args), remaining); } }}