/** * underscore 节流函数,返回函数连续调用时,func 执行频率限定为 次 / wait * * @param{function}func回调函数 * @param{number}wait表示时间窗口的间隔 * @param{object}options如果想忽略开始函数的的调用,传入{leading: false} 。*如果想忽略结尾函数的调用,传入{trailing: false} *两者不能共存,否则函数不能执行 * @return {function}返回客户调用函数*/_.throttle = function(func, wait, options) {var context, args, result;var timeout = null;// 之前的时间戳var previous = 0;// 如果 options 没传则设为空对象if (!options) options = {};// 定时器回调函数var later = function() {// 如果设置了 leading,就将 previous 设为 0// 用于下面函数的第一个 if 判断previous = options.leading === false ? 0 : _.now();// 置空一是为了防止内存泄漏,二是为了下面的定时器判断timeout = null;result = func.apply(context, args);if (!timeout) context = args = null;};return function() {// 获得当前时间戳var now = _.now();// 首次进入前者肯定为 true// 如果需要第一次不执行函数// 就将上次时间戳设为当前的// 这样在接下来计算 remaining 的值时会大于0if (!previous && options.leading === false) previous = now;// 计算剩余时间var remaining = wait - (now - previous);context = this;args = arguments;// 如果当前调用已经大于上次调用时间 + wait// 或者用户手动调了时间// 如果设置了 trailing,只会进入这个条件// 如果没有设置 leading,那么第一次会进入这个条件// 还有一点,你可能会觉得开启了定时器那么应该不会进入这个 if 条件了// 其实还是会进入的,因为定时器的延时// 并不是准确的时间,很可能你设置了2秒// 但是他需要2.2秒才触发,这时候就会进入这个条件if (remaining <= 0 || remaining > wait) {// 如果存在定时器就清理掉否则会调用二次回调if (timeout) {clearTimeout(timeout);timeout = null;}previous = now;result = func.apply(context, args);if (!timeout) context = args = null;} else if (!timeout && options.trailing !== false) {// 判断是否设置了定时器和 trailing// 没有的话就开启一个定时器// 并且不能不能同时设置 leading 和 trailingtimeout = setTimeout(later, remaining);}return result;};};Event Busclass Events {constructor() {this.events = new Map();}addEvent(key, fn, isOnce, ...args) {const value = https://www.isolves.com/it/cxkf/cxy/2021-04-09/this.events.get(key) ? this.events.get(key) : this.events.set(key, new Map()).get(key)value.set(fn, (...args1) => {fn(...args, ...args1)isOnce && this.off(key, fn)})}on(key, fn, ...args) {if (!fn) {console.error(`没有传入回调函数`);return}this.addEvent(key, fn, false, ...args)}fire(key, ...args) {if (!this.events.get(key)) {console.warn(`没有 ${key} 事件`);return;}for (let [, cb] of this.events.get(key).entries()) {cb(...args);}}off(key, fn) {if (this.events.get(key)) {this.events.get(key).delete(fn);}}once(key, fn, ...args) {this.addEvent(key, fn, true, ...args)}}instanceofinstanceof 可以正确的判断对象的类型,因为内部机制是通过判断对象的原型链中是不是能找到类型的 prototype 。function instanceof(left, right) {// 获得类型的原型let prototype = right.prototype// 获得对象的原型left = left.__proto__// 判断对象的类型是否等于类型的原型while (true) {if (left === null)return falseif (prototype === left)return trueleft = left.__proto__}}callFunction.prototype.myCall = function(context, ...args) {context = context || windowlet fn = Symbol()context[fn] = thislet result = context[fn](...args)delete context[fn]return result}applyFunction.prototype.myApply = function(context) {context = context || windowlet fn = Symbol()context[fn] = thislet resultif (arguments[1]) {result = context[fn](...arguments[1])} else {result = context[fn]()}delete context[fn]return result}bindFunction.prototype.myBind = function (context) {var _this = thisvar args = [...arguments].slice(1)// 返回一个函数return function F() {// 因为返回了一个函数,我们可以 new F(),所以需要判断if (this instanceof F) {return new _this(...args, ...arguments)}return _this.apply(context, args.concat(...arguments))}}其他其他手写题上文已经有提及,比如模拟 new、ES5 实现继承、深拷贝 。另外大家可能经常能看到手写 Promise 的文章,其实根据笔者目前收集到的数百道面试题以及读者的反馈来看,压根就没人遇到这个考点,所以我们大可不必在这上面花时间 。
【17K star 仓库,解决 90% 的大厂基础面试题】
推荐阅读
- 不爱跳槽的程序员集中在8-17k,揭晓中国开发者的真实现状
- 大型Git仓库的部分克隆
- 数据仓库、数据集市、数据湖、数据中台这些概念,终于整明白了
- 银行数据仓库的系统架构是什么?看这篇足矣
- 数据仓库构建流程
- 长安|性价比超宏光MINI!长安奔奔E-Star国民版暂停收取订单:产能受限
- 数据仓库组件:Hive环境搭建和基础用法
- 10分钟自建企业级Docker镜像仓库!这个开源项目太顶了
- 一文带你搭建本地YUM仓库
- 开发框架搭建考量
