• 前端常见手撕源码


    转载于 https://mp.weixin.qq.com/s/PAjuynQwL9wn-v8zxKgNvA

    个人觉得总结的很好,可以琢磨,故而转载一下,如若侵权请告知删除~~~

    前言

    本文主要总结了 2021 年前端提前批和秋招所考察的手写题,题目来源于牛客网前端面经区,统计时间自 3 月初至 10 月底,面经来源于阿里、腾讯、百度、字节、美团、京东、快手、拼多多等 15 家公司,并做了简单的频次划分。

    • ⭐⭐⭐⭐⭐: 在 15 家公司面试中出现 10+
    • ⭐⭐⭐⭐:在 15 家公式面试中出现 5-10
    • ⭐⭐⭐:在 15 家公司面试中出现 3-5
    • 无星:出现 1-2

    题目解析一部分来源于小包的编写,另一部分如果我感觉题目扩展开来更好的话,我就选取部分大佬的博客链接。

    promise

    实现promise

    考察频率: (⭐⭐⭐⭐⭐)

    参考代码[1]

    实现promise.all

    考察频率: (⭐⭐⭐⭐⭐)

    function PromiseAll(promises){
        return new Promise((resolve, reject)=>{
            if(!Array.isArray(promises)){
                throw new TypeError("promises must be an array")
            }
            let result = [] 
            let count = 
            promises.forEach((promise, index) => {
                promise.then((res)=>{
                    result[index] = res
                    count++
                    count === promises.length && resolve(result) 
                }, (err)=>{
                    reject(err)
                })
            })
        })
    }


    复制代码

    实现 promise.finally

    考察频率: (⭐⭐⭐⭐⭐)

    Promise.prototype.finally = function (cb) {
      return this.then(function (value) {
        return Promise.resolve(cb()).then(function () {
          return value
        })
      }, function (err) {
        return Promise.resolve(cb()).then(function () {
          throw err
        })
      })
    }

    复制代码

    实现promise.allSettled

    考察频率: (⭐⭐⭐⭐)

    function allSettled(promises) {
      if (promises.length === 0) return Promise.resolve([])
      
      const _promises = promises.map(
        item => item instanceof Promise ? item : Promise.resolve(item)
        )
      
      return new Promise((resolve, reject) => {
        const result = []
        let unSettledPromiseCount = _promises.length
        
        _promises.forEach((promise, index) => {
          promise.then((value) => {
            result[index] = {
              status: 'fulfilled',
              value
            }
            
            unSettledPromiseCount -= 1
            // resolve after all are settled
            if (unSettledPromiseCount === 0) {
              resolve(result)
            }
          }, (reason) => {
            result[index] = {
              status: 'rejected',
              reason
            }
            
            unSettledPromiseCount -= 1
            // resolve after all are settled
            if (unSettledPromiseCount === 0) {
              resolve(result)
            }
          })
        })
      })
    }
    复制代码

    实现promise.race

    考察频率: (⭐⭐⭐)

    Promise.race = function(promiseArr) {
        return new Promise((resolve, reject) => {
            promiseArr.forEach(p => {
                Promise.resolve(p).then(val => {
                    resolve(val)
                }, err => {
                    rejecte(err)
                })
            })
        })
    }
    复制代码

    来说一下如何串行执行多个Promise

    参考代码[2]

    promise.any

    Promise.any = function(promiseArr) {
        let index = 0
        return new Promise((resolve, reject) => {
            if (promiseArr.length === 0) return 
            promiseArr.forEach((p, i) => {
                Promise.resolve(p).then(val => {
                    resolve(val)
                    
                }, err => {
                    index++
                    if (index === promiseArr.length) {
                      reject(new AggregateError('All promises were rejected'))
                    }
                })
            })
        })
    }


    复制代码

    resolve

    Promise.resolve = function(value) {
        if(value instanceof Promise){
            return value
        }
        return new Promise(resolve => resolve(value))
    }

    复制代码

    reject

    Promise.reject = function(reason) {
        return new Promise((resolve, reject) => reject(reason))
    }
    复制代码

    Array篇

    数组去重

    考察频率: (⭐⭐⭐⭐⭐)

    使用双重 for 和 splice

    function unique(arr){            
        for(var i=0; i<arr.length; i++){
            for(var j=i+1; j<arr.length; j++){
                if(arr[i]==arr[j]){         
                //第一个等同于第二个,splice方法删除第二个
                    arr.splice(j,1);
                    // 删除后注意回调j
                    j--;
                }
            }
        }
    return arr;
    }
    复制代码

    使用 indexOf 或 includes 加新数组

    //使用indexof
    function unique(arr) {
        var uniqueArr = []; // 新数组
        for (let i = 0; i < arr.length; i++) {
            if (uniqueArr.indexOf(arr[i]) === -1) {
                //indexof返回-1表示在新数组中不存在该元素
                uniqueArr.push(arr[i])//是新数组里没有的元素就push入
            }
        }
        return uniqueArr;
    }
    // 使用includes
    function unique(arr) {
        var uniqueArr = []; 
        for (let i = 0; i < arr.length; i++) {
            //includes 检测数组是否有某个值
            if (!uniqueArr.includes(arr[i])) {
                uniqueArr.push(arr[i])//
            }
        }
        return uniqueArr;
    }
    复制代码

    sort 排序后,使用快慢指针的思想

    function unique(arr) {
        arr.sort((a, b) => a - b);
        var slow = 1,
            fast = 1;
        while (fast < arr.length) {
            if (arr[fast] != arr[fast - 1]) {
                arr[slow ++] = arr[fast];
            }
            ++ fast;
        }
        arr.length = slow;
        return arr;
    }
    复制代码

    sort 方法用于从小到大排序(返回一个新数组),其参数中不带以上回调函数就会在两位数及以上时出现排序错误(如果省略,元素按照转换为的字符串的各个字符的 Unicode 位点进行排序。两位数会变为长度为二的字符串来计算)。

    ES6 提供的 Set 去重

    function unique(arr) {
        const result = new Set(arr);
        return [...result];
        //使用扩展运算符将Set数据结构转为数组
    }
    复制代码

    Set 中的元素只会出现一次,即 Set 中的元素是唯一的。

    使用哈希表存储元素是否出现(ES6 提供的 map)

    function unique(arr) {
        let map = new Map();
        let uniqueArr = new Array();  // 数组用于返回结果
        for (let i = 0; i < arr.length; i++) {
          if(map.has(arr[i])) {  // 如果有该key值
            map.set(arr[i], true); 
          } else { 
            map.set(arr[i], false);   // 如果没有该key值
            uniqueArr.push(arr[i]);
          }
        } 
        return uniqueArr ;
    }
    复制代码

    map 对象保存键值对,与对象类似。但 map 的键可以是任意类型,对象的键只能是字符串类型。

    如果数组中只有数字也可以使用普通对象作为哈希表。

    filter 配合 indexOf

    function unique(arr) {
        return arr.filter(function (item, index, arr) {
            //当前元素,在原始数组中的第一个索引==当前索引值,否则返回当前元素
            //不是那么就证明是重复项,就舍弃
            return arr.indexOf(item) === index;
        })
    }
    复制代码

    这里有可能存在疑问,我来举个例子:

    const arr = [1,1,2,1,3]
    arr.indexOf(arr[0]) === // 1 的第一次出现
    arr.indexOf(arr[1]) !== // 说明前面曾经出现过1
    复制代码

    reduce 配合 includes

    function unique(arr){
        let uniqueArr = arr.reduce((acc,cur)=>{
            if(!acc.includes(cur)){
                acc.push(cur);
            }
            return acc;
        },[]) // []作为回调函数的第一个参数的初始值
        return uniqueArr
    }
    复制代码

    数组扁平化

    考察频率: (⭐⭐⭐)

    参考代码[3]

    forEach

    考察频率: (⭐⭐⭐)

    Array.prototype.myForEach = function (callbackFn) {
        // 判断this是否合法
        if (this === null || this === undefined) {
            throw new TypeError("Cannot read property 'myForEach' of null");
        }
        // 判断callbackFn是否合法
        if (Object.prototype.toString.call(callbackFn) !== "[object Function]") {
            throw new TypeError(callbackFn + ' is not a function')
        }
        // 取到执行方法的数组对象和传入的this对象
        var _arr = this, thisArg = arguments[1] || window;
        for (var i = 0; i < _arr.length; i++) {
            // 执行回调函数
            callbackFn.call(thisArg, _arr[i], i, _arr);
        }
    }


    复制代码

    reduce

    考察频率: (⭐⭐⭐)

    Array.prototype.myReduce = function(callbackFn) {
        var _arr = this, accumulator = arguments[1];
        var i = 0;
        // 判断是否传入初始值
        if (accumulator === undefined) {
            // 没有初始值的空数组调用reduce会报错
            if (_arr.length === 0) {
                throw new Error('initVal and Array.length>0 need one')
            }
            // 初始值赋值为数组第一个元素
            accumulator = _arr[i];
            i++;
        }
        for (; i<_arr.length; i++) {
            // 计算结果赋值给初始值
            accumulator = callbackFn(accumulator,  _arr[i], i, _arr)
        }
        return accumulator;
    }

    复制代码

    map

    Array.prototype.myMap = function(callbackFn) {
        var _arr = this, thisArg = arguments[1] || window, res = [];
        for (var i = 0; i<_arr.length; i++) {
            // 存储运算结果
            res.push(callbackFn.call(thisArg, _arr[i], i, _arr));
        }
        return res;
    }

    复制代码

    filter

    Array.prototype.myFilter = function(callbackFn) {
        var _arr = this, thisArg = arguments[1] || window, res = [];
        for (var i = 0; i<_arr.length; i++) {
            // 回调函数执行为true
            if (callbackFn.call(thisArg, _arr[i], i, _arr)) {
                res.push(_arr[i]);
            }
        }
        return res;
    }
    复制代码

    every

    Array.prototype.myEvery = function(callbackFn) {
        var _arr = this, thisArg = arguments[1] || window;
        // 开始标识值为true
        // 遇到回调返回false,直接返回false
        // 如果循环执行完毕,意味着所有回调返回值为true,最终结果为true
        var flag = true;
        for (var i = 0; i<_arr.length; i++) {
            // 回调函数执行为false,函数中断
            if (!callbackFn.call(thisArg, _arr[i], i, _arr)) {
                return false;
            }
        }
        return flag;
    }
    复制代码

    some

    Array.prototype.mySome = function(callbackFn) {
        var _arr = this, thisArg = arguments[1] || window;
        // 开始标识值为false
        // 遇到回调返回true,直接返回true
        // 如果循环执行完毕,意味着所有回调返回值为false,最终结果为false
        var flag = false;
        for (var i = 0; i<_arr.length; i++) {
            // 回调函数执行为false,函数中断
            if (callbackFn.call(thisArg, _arr[i], i, _arr)) {
                return true;
            }
        }
        return flag;
    }
    复制代码

    find/findIndex

    Array.prototype.myFind = function(callbackFn) {
        var _arr = this, thisArg = arguments[1] || window;
        // 遇到回调返回true,直接返回该数组元素
        // 如果循环执行完毕,意味着所有回调返回值为false,最终结果为undefined
        for (var i = 0; i<_arr.length; i++) {
            // 回调函数执行为false,函数中断
            if (callbackFn.call(thisArg, _arr[i], i, _arr)) {
                return _arr[i];
            }
        }
        return undefined;
    }
    复制代码

    indexOf

    function indexOf(findVal, beginIndex = 0) {
        if (this.length < 1 || beginIndex > findVal.length) {
            return -1;
        }
        if (!findVal) {
            return 0;
        }
        beginIndex = beginIndex <= 0 ? 0 : beginIndex;
        for (let i = beginIndex; i < this.length; i++) {
            if (this[i] == findVal) return i;
        }
        return -1;
    }
          

    复制代码

    实现sort

    参考代码[4]

    防抖节流

    实现防抖函数debounce

    考察频率: (⭐⭐⭐⭐⭐)

    function debounce(func, wait, immediate) {

        var timeout, result;

        var debounced = function () {
            var context = this;
            var args = arguments;

            if (timeout) clearTimeout(timeout);
            if (immediate) {
                // 如果已经执行过,不再执行
                var callNow = !timeout;
                timeout = setTimeout(function(){
                    timeout = null;
                }, wait)
                if (callNow) result = func.apply(context, args)
            }
            else {
                timeout = setTimeout(function(){
                    result = func.apply(context, args)
                }, wait);
            }
            return result;
        };

        debounced.cancel = function() {
            clearTimeout(timeout);
            timeout = null;
        };

        return debounced;
    }
    复制代码

    实现节流函数throttle

    考察频率: (⭐⭐⭐⭐⭐)

    // 第四版
    function throttle(func, wait, options) {
        var timeout, context, args, result;
        var previous = 0;
        if (!options) options = {};

        var later = function() {
            previous = options.leading === false ? 0 : new Date().getTime();
            timeout = null;
            func.apply(context, args);
            if (!timeout) context = args = null;
        };

        var throttled = function() {
            var now = new Date().getTime();
            if (!previous && options.leading === false) previous = now;
            var remaining = wait - (now - previous);
            context = this;
            args = arguments;
            if (remaining <= 0 || remaining > wait) {
                if (timeout) {
                    clearTimeout(timeout);
                    timeout = null;
                }
                previous = now;
                func.apply(context, args);
                if (!timeout) context = args = null;
            } else if (!timeout && options.trailing !== false) {
                timeout = setTimeout(later, remaining);
            }
        };
        return throttled;
    }

    复制代码

    ⛲ Object篇

    能不能写一个完整的深拷贝

    考察频率: (⭐⭐⭐⭐⭐)

    const getType = obj => Object.prototype.toString.call(obj);

    const isObject = (target) => (typeof target === 'object' || typeof target === 'function') && target !== null;

    const canTraverse = {
      '[object Map]': true,
      '[object Set]': true,
      '[object Array]': true,
      '[object Object]': true,
      '[object Arguments]': true,
    };
    const mapTag = '[object Map]';
    const setTag = '[object Set]';
    const boolTag = '[object Boolean]';
    const numberTag = '[object Number]';
    const stringTag = '[object String]';
    const symbolTag = '[object Symbol]';
    const dateTag = '[object Date]';
    const errorTag = '[object Error]';
    const regexpTag = '[object RegExp]';
    const funcTag = '[object Function]';

    const handleRegExp = (target) => {
      const { source, flags } = target;
      return new target.constructor(source, flags);
    }

    const handleFunc = (func) => {
      // 箭头函数直接返回自身
      if(!func.prototype) return func;
      const bodyReg = /(?<={)(.|\n)+(?=})/m;
      const paramReg = /(?<=\().+(?=\)\s+{)/;
      const funcString = func.toString();
      // 分别匹配 函数参数 和 函数体
      const param = paramReg.exec(funcString);
      const body = bodyReg.exec(funcString);
      if(!body) return null;
      if (param) {
        const paramArr = param[0].split(',');
        return new Function(...paramArr, body[0]);
      } else {
        return new Function(body[0]);
      }
    }

    const handleNotTraverse = (target, tag) => {
      const Ctor = target.constructor;
      switch(tag) {
        case boolTag:
          return new Object(Boolean.prototype.valueOf.call(target));
        case numberTag:
          return new Object(Number.prototype.valueOf.call(target));
        case stringTag:
          return new Object(String.prototype.valueOf.call(target));
        case symbolTag:
          return new Object(Symbol.prototype.valueOf.call(target));
        case errorTag: 
        case dateTag:
          return new Ctor(target);
        case regexpTag:
          return handleRegExp(target);
        case funcTag:
          return handleFunc(target);
        default:
          return new Ctor(target);
      }
    }

    const deepClone = (target, map = new WeakMap()) => {
      if(!isObject(target)) 
        return target;
      let type = getType(target);
      let cloneTarget;
      if(!canTraverse[type]) {
        // 处理不能遍历的对象
        return handleNotTraverse(target, type);
      }else {
        // 这波操作相当关键,可以保证对象的原型不丢失!
        let ctor = target.constructor;
        cloneTarget = new ctor();
      }

      if(map.get(target)) 
        return target;
      map.set(target, true);

      if(type === mapTag) {
        //处理Map
        target.forEach((item, key) => {
          cloneTarget.set(deepClone(key, map), deepClone(item, map));
        })
      }
      
      if(type === setTag) {
        //处理Set
        target.forEach(item => {
          cloneTarget.add(deepClone(item, map));
        })
      }

      // 处理数组和对象
      for (let prop in target) {
        if (target.hasOwnProperty(prop)) {
            cloneTarget[prop] = deepClone(target[prop], map);
        }
      }
      return cloneTarget;
    }

    复制代码

    参考博客[5]

    实现new

    考察频率: (⭐⭐⭐⭐)

    function createObject(Con) {
        // 创建新对象obj
        // var obj = {};也可以
        var obj = Object.create(null);

        // 将obj.__proto__ -> 构造函数原型
        // (不推荐)obj.__proto__ = Con.prototype
        Object.setPrototypeOf(obj, Con.prototype);

        // 执行构造函数,并接受构造函数返回值
        const ret = Con.apply(obj, [].slice.call(arguments, 1));

        // 若构造函数返回值为对象,直接返回该对象
        // 否则返回obj
        return typeof(ret) === 'object' ? ret: obj;
    }
    复制代码

    继承

    考察频率: (⭐⭐⭐⭐)

    原型链继承

    借用构造函数(经典继承)

    组合继承

    原型式继承

    寄生式继承

    寄生组合式继承

    Class实现继承(补充一下)

    class Animal {
        constructor(name) {
            this.name = name
        } 
        getName() {
            return this.name
        }
    }
    class Dog extends Animal {
        constructor(name, age) {
            super(name)
            this.age = age
        }
    }
    复制代码

    参考代码[6]

    实现object.create

    function newCreate(proto, propertiesObject) {
        if (typeof proto !== 'object' && typeof proto !== 'function') {
            throw TypeError('Object prototype may only be an Object: ' + proto)
        }
        function F() { }
        F.prototype = proto
        const o = new F()

        if (propertiesObject !== undefined) {
            Object.keys(propertiesObject).forEach(prop => {
                let desc = propertiesObject[prop]
                if (typeof desc !== 'object' || desc === null) {
                    throw TypeError('Object prorotype may only be an Object: ' + desc)
                } else {
                    Object.defineProperty(o, prop, desc)
                }
            })
        }

        return o
    }
    复制代码

    Function篇

    call

    考察频率: (⭐⭐⭐⭐)

    Function.prototype.myCall = function (thisArg) {
        thisArg = thisArg || window;
        thisArg.func = this;
        const args = []
        for (let i = 1; i<arguments.length; i++) {
            args.push('arguments['+ i + ']')
        }
        const result = eval('thisArg.func(' + args +')')
        delete thisArg.func;
        return result;
    }
    复制代码

    bind

    考察频率: (⭐⭐⭐⭐)

    Function.prototype.sx_bind = function (obj, ...args) {
        obj = obj || window

        const fn = Symbol()
        obj[fn] = this
        const _this = this

        const res = function (...innerArgs) {
            console.log(this, _this)
            if (this instanceof _this) {
                this[fn] = _this
                this[fn](...[...args, ...innerArgs])
                delete this[fn]
            } else {
                obj[fn](...[...args, ...innerArgs])
                delete obj[fn]
            }
        }
        res.prototype = Object.create(this.prototype)
        return res
    }
    复制代码

    apply

    考察频率: (⭐⭐⭐⭐)

    Function.prototype.myApply = function (thisArg, arr) {
        thisArg = thisArg || window;
        thisArg.func = this;
        const args = []
        for (let i = 0; i<arr.length; i++) {
            args.push('arr['+ i + ']')
        }
        const result = eval('thisArg.func(' + args +')')
        delete thisArg.func;
        return result;
    }
    复制代码

    实现柯里化

    考察频率: (⭐⭐⭐)

    参考代码[7]

    实现链式调用

    参考代码[8]

    偏函数

    参考代码[9]

    ajax 与 jsonp

    考察频率: (⭐⭐⭐)

    实现ajax

    function ajax({
        url= null,
     method = 'GET',
     dataType = 'JSON',
     async = true}){
     return new Promise((resolve, reject) => {
      let xhr = new XMLHttpRequest()
      xhr.open(method, url, async)
      xhr.responseType = dataType
      xhr.onreadystatechange = () => {
       if(!/^[23]\d{2}$/.test(xhr.status)) return;
       if(xhr.readyState === 4) {
        let result = xhr.responseText
        resolve(result)
       }
      }
      xhr.onerror = (err) => {
       reject(err)
      }
      xhr.send()
     })
    }
    复制代码

    实现jsonp

    const jsonp = ({ url, params, callbackName }) => {
        const generateUrl = () => {
            let dataSrc = ''
            for (let key in params) {
                if (params.hasOwnProperty(key)) {
                    dataSrc += `${key}=${params[key]}&`
                }
            }
            dataSrc += `callback=${callbackName}`
            return `${url}?${dataSrc}`
        }
        return new Promise((resolve, reject) => {
            const scriptEle = document.createElement('script')
            scriptEle.src = generateUrl()
            document.body.appendChild(scriptEle)
            window[callbackName] = data => {
                resolve(data)
                document.removeChild(scriptEle)
            }
        })
    }
    复制代码

    ES6篇

    实现set

    class Set {
      constructor() {
        this.items = {};
        this.size = 0;
      }

      has(element) {
        return element in this.items;
      }

      add(element) {
        if(! this.has(element)) {
          this.items[element] = element;
          this.size++;
        }
        return this;
      }

      delete(element) {
        if (this.has(element)) {
          delete this.items[element];
          this.size--;
        }
        return this;
      }

      clear() {
        this.items = {}
        this.size = 0;
      }

      values() {
        let values = [];
        for(let key in this.items) {
          if(this.items.hasOwnProperty(key)) {
            values.push(key);
          }
        }
        return values;
      }
    }
    复制代码

    实现 map

    function defaultToString(key) {
      if(key === null) {
        return 'NULL';
      } else if (key === undefined) {
        return 'UNDEFINED'
      } else if (Object.prototype.toString.call(key) === '[object Object]' || Object.prototype.toString.call(key) === '[object Array]') {
        return JSON.stringify(key);
      }
      return key.toString();
    }

    class Map {
      constructor() {
        this.items = {};
        this.size = 0;
      }

      set(key, value) {
        if(!this.has(key)) {
          this.items[defaultToString(key)] = value;
          this.size++;
        }
        return this;
      }

      get(key) {
        return this.items[defaultToString(key)];
      }

      has(key) {
        return this.items[defaultToString(key)] !== undefined;
      }

      delete(key) {
        if (this.has(key)) {
          delete this.items[key];
          this.size--;
        }
        return this;
      }

      clear() {
        this.items = {}
        this.size = 0;
      }

      keys() {
        let keys = [];
        for(let key in this.items) {
          if(this.has(key)) {
            keys.push(key)
          }
        }
        return keys;
      }

      values() {
        let values = [];
        for(let key in this.items) {
          if(this.has(key)) {
            values.push(this.items[key]);
          }
        }
        return values;
      }
    }
    复制代码

    实现es6的class

    参考代码[10]

    其他

    instanceof

    考察频率: (⭐⭐⭐⭐)

    function instance_of(Case, Constructor) {
        // 基本数据类型返回false
        // 兼容一下函数对象
        if ((typeof(Case) != 'object' && typeof(Case) != 'function') || Case == 'null') return false;
        let CaseProto = Object.getPrototypeOf(Case);
        while (true) {
            // 查到原型链顶端,仍未查到,返回false
            if (CaseProto == null) return false;
            // 找到相同的原型
            if (CaseProto === Constructor.prototype) return true;
            CaseProto = Object.getPrototypeOf(CaseProto);
        }
    }
    复制代码

    实现千分位分隔符

    考察频率: (⭐⭐⭐)

    var str = "100000000000",
        reg = /(?=(\B\d{3})+$)/g;
    str.replace(reg, ",")
    复制代码

    把一个JSON对象的key从下划线形式(Pascal)转换到小驼峰形式(Camel)

    考察频率: (⭐⭐⭐)

    参考代码[11]

    实现数据类型判断函数

    function myTypeof(obj) {
       return Object.prototype.toString.call(obj).slice(8, -1).toLowerCase() 
    }
    复制代码

    实现数组转树

    参考代码[12]

    实现sleep函数

    // promise
    const sleep = time => {
      return new Promise(resolve => setTimeout(resolve,time))
    }
    sleep(1000).then(()=>{
      console.log(1)
    })
    // ES5
    function sleep(callback,time) {
      if(typeof callback === 'function')
        setTimeout(callback,time)
    }

    function output(){
      console.log(1);
    }
    sleep(output,1000);

    复制代码

    实现发布订阅模式

    class EventEmitter {
        constructor() {
            this.cache = {}
        }
        on(name, fn) {
            if (this.cache[name]) {
                this.cache[name].push(fn)
            } else {
                this.cache[name] = [fn]
            }
        }
        off(name, fn) {
            let tasks = this.cache[name]
            if (tasks) {
                const index = tasks.findIndex(f => f === fn || f.callback === fn)
                if (index >= 0) {
                    tasks.splice(index, 1)
                }
            }
        }
        emit(name, once = false, ...args) {
            if (this.cache[name]) {
                // 创建副本,如果回调函数内继续注册相同事件,会造成死循环
                let tasks = this.cache[name].slice()
                for (let fn of tasks) {
                    fn(...args)
                }
                if (once) {
                    delete this.cache[name]
                }
            }
        }
    }
    复制代码

     

  • 相关阅读:
    在宝塔中升级mysql版本
    测试winform程序到树莓派运行
    winserver2012远程桌面进入只有CMD窗口,无桌面解决方法
    一七年春末
    Linux 上通过rpm安装mysql
    Linux 上关于iptables
    Linux环境下安装JDK
    Linux上安装tomcat
    Linux 下安装redis
    Map集合按照value和key进行排序
  • 原文地址:https://www.cnblogs.com/wulinzi/p/15701526.html
Copyright © 2020-2023  润新知