17K star 仓库,解决 90% 的大厂基础面试题( 八 )

  • 对于延时执行函数来说的实现:清除定时器ID,如果是延迟调用就调用函数
  • 节流防抖动和节流本质是不一样的 。防抖动是将多次执行变为最后一次执行,节流是将多次执行变成每隔一段时间执行 。
    /** * 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% 的大厂基础面试题】


    推荐阅读