美文网首页
前端面试造火箭(手写代码)

前端面试造火箭(手写代码)

作者: 糖醋鱼_ | 来源:发表于2020-09-28 17:17 被阅读0次
    1、防抖
    function debounce(func, ms = 1000) {
      let timer;
      return function (...args) {
        if (timer) {
          clearTimeout(timer)
        }
        timer = setTimeout(() => {
          func.apply(this, args)
        }, ms)
      }
    }
    
    // 测试
    const task = () => { console.log('run task') }
    const debounceTask = debounce(task, 1000)
    window.addEventListener('scroll', debounceTask)
    
    2、节流
    function throttle(func, ms = 1000) {
     let canRun = true
     return function (...args) {
       if (!canRun) return
       canRun = false
       setTimeout(() => {
         func.apply(this, args)
         canRun = true
       }, ms)
     }
    }
    
    // 测试
    const task = () => { console.log('run task') }
    const throttleTask = throttle(task, 1000)
    window.addEventListener('scroll', throttleTask)
    
    3、call
    Function.prototype.myCall = function (context = globalThis) {
      // 关键步骤,在 context 上调用方法,触发 this 绑定为 context,使用 Symbol 防止原有属性的覆盖
      const key = Symbol('key')
      context[key] = this
      let args = [].slice.call(arguments, 1)
      let res = context[key](...args)
      delete context[key]
      return res
    };
    
    // 测试
    const me = { name: 'Jack' }
    function say() {
      console.log(`My name is ${this.name || 'default'}`);
    }
    say.myCall(me)
    
    4、bind
    Function.prototype.myBind = function (context = globalThis) {
      const fn = this
      const args = Array.from(arguments).slice(1)
      const newFunc = function () {
        const newArgs = args.concat(...arguments)
        if (this instanceof newFunc) {
          // 通过 new 调用,绑定 this 为实例对象
          fn.apply(this, newArgs)
        } else {
          // 通过普通函数形式调用,绑定 context
          fn.apply(context, newArgs)
        }
      }
      // 支持 new 调用方式
      newFunc.prototype = Object.create(fn.prototype)
      return newFunc
    }
    
    // 测试
    const me = { name: 'Jack' }
    const other = { name: 'Jackson' }
    function say() {
      console.log(`My name is ${this.name || 'default'}`);
    }
    const meSay = say.bind(me)
    meSay()
    const otherSay = say.bind(other)
    otherSay()
    
    5、apply
    Function.prototype.myApply = function (context = globalThis) {
      // 关键步骤,在 context 上调用方法,触发 this 绑定为 context,使用 Symbol 防止原有属性的覆盖
      const key = Symbol('key')
      context[key] = this
      let res
      if (arguments[1]) {
        res = context[key](...arguments[1])
      } else {
        res = context[key]()
      }
      delete context[key]
      return res
    }
    
    // 测试
    const me = { name: 'Jack' }
    function say() {
      console.log(`My name is ${this.name || 'default'}`);
    }
    say.myApply(me)
    
    6、deepCopy
    function deepCopy(obj, cache = new WeakMap()) {
      if (!obj instanceof Object) return obj
      // 防止循环引用
      if (cache.get(obj)) return cache.get(obj)
      // 支持函数
      if (obj instanceof Function) {
        return function () {
          obj.apply(this, arguments)
        }
      }
      // 支持日期
      if (obj instanceof Date) return new Date(obj)
      // 支持正则对象
      if (obj instanceof RegExp) return new RegExp(obj.source, obj.flags)
      // 还可以增加其他对象,比如:Map, Set等,根据情况判断增加即可,面试点到为止就可以了
    
      // 数组是 key 为数字素银的特殊对象
      const res = Array.isArray(obj) ? [] : {}
      // 缓存 copy 的对象,用于处理循环引用的情况
      cache.set(obj, res)
    
      Object.keys(obj).forEach((key) => {
        if (obj[key] instanceof Object) {
          res[key] = deepCopy(obj[key], cache)
        } else {
          res[key] = obj[key]
        }
      });
      return res
    }
    
    // 测试
    const source = {
      name: 'Jack',
      meta: {
        age: 12,
        birth: new Date('1997-10-10'),
        ary: [1, 2, { a: 1 }],
        say() {
          console.log('Hello');
        }
      }
    }
    source.source = source
    const newObj = deepCopy(source)
    console.log(newObj.meta.ary[2] === source.meta.ary[2]);
    
    7、 观察者模式 | 发布订阅模式
    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) {
        const 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) {
        if (this.cache[name]) {
          // 创建副本,如果回调函数内继续注册相同事件,会造成死循环
          const tasks = this.cache[name].slice()
          for (let fn of tasks) {
            fn();
          }
        }
      }
    
      emit(name, once = false) {
        if (this.cache[name]) {
          // 创建副本,如果回调函数内继续注册相同事件,会造成死循环
          const tasks = this.cache[name].slice()
          for (let fn of tasks) {
            fn();
          }
          if (once) {
            delete this.cache[name]
          }
        }
      }
    }
    // 测试
    const eventBus = new EventEmitter()
    const task1 = () => { console.log('task1'); }
    const task2 = () => { console.log('task2'); }
    eventBus.on('task', task1)
    eventBus.on('task', task2)
    
    setTimeout(() => {
      eventBus.emit('task')
    }, 1000)
    
    8、new
    function myNew(Func, ...args) {
      const instance = {};
      if (Func.prototype) {
        Object.setPrototypeOf(instance, Func.prototype)
      }
      const res = Func.apply(instance, args)
      if (typeof res === "function" || (typeof res === "object" && res !== null)) {
        return res
      }
      return instance
    }
    
    // 测试
    function Person(name) {
      this.name = name
    }
    Person.prototype.sayName = function() {
      console.log(`My name is ${this.name}`)
    }
    const me = myNew(Person, 'Jack')
    me.sayName()
    console.log(me)
    
    9、promise
    // 建议阅读 [Promises/A+ 标准](https://promisesaplus.com/)
    class MyPromise {
      constructor(func) {
        this.status = 'pending'
        this.value = null
        this.resolvedTasks = []
        this.rejectedTasks = []
        this._resolve = this._resolve.bind(this)
        this._reject = this._reject.bind(this)
        try {
          func(this._resolve, this._reject)
        } catch (error) {
          this._reject(error)
        }
      }
    
      _resolve(value) {
        setTimeout(() => {
          this.status = 'fulfilled'
          this.value = value
          this.resolvedTasks.forEach(t => t(value))
        })
      }
    
      _reject(reason) {
        setTimeout(() => {
          this.status = 'reject'
          this.value = reason
          this.rejectedTasks.forEach(t => t(reason))
        })
      }
    
      then(onFulfilled, onRejected) {
        return new MyPromise((resolve, reject) => {
          this.resolvedTasks.push((value) => {
            try {
              const res = onFulfilled(value)
              if (res instanceof MyPromise) {
                res.then(resolve, reject)
              } else {
                resolve(res)
              }
            } catch (error) {
              reject(error)
            }
          })
          this.rejectedTasks.push((value) => {
            try {
              const res = onRejected(value)
              if (res instanceof MyPromise) {
                res.then(resolve, reject)
              } else {
                reject(res)
              }
            } catch (error) {
              reject(error)
            }
          })
        })
      }
    
      catch(onRejected) {
        return this.then(null, onRejected);
      }
    }
    
    // 测试
    new MyPromise((resolve) => {
      setTimeout(() => {
        resolve(1);
      }, 500);
    }).then((res) => {
        console.log(res);
        return new MyPromise((resolve) => {
          setTimeout(() => {
            resolve(2);
          }, 500);
        });
      }).then((res) => {
        console.log(res);
        throw new Error('a error')
      }).catch((err) => {
        console.log('==>', err);
      })
    
    10、图片懒加载
    // <img src="default.png" data-src="https://xxxx/real.png">
    function isVisible(el) {
      const position = el.getBoundingClientRect()
      const windowHeight = document.documentElement.clientHeight
      // 顶部边缘可见
      const topVisible = position.top > 0 && position.top < windowHeight;
      // 底部边缘可见
      const bottomVisible = position.bottom < windowHeight && position.bottom > 0;
      return topVisible || bottomVisible;
    }
    
    function imageLazyLoad() {
      const images = document.querySelectorAll('img')
      for (let img of images) {
        const realSrc = img.dataset.src
        if (!realSrc) continue
        if (isVisible(img)) {
          img.src = realSrc
          img.dataset.src = ''
        }
      }
    }
    
    // 测试
    window.addEventListener('load', imageLazyLoad)
    window.addEventListener('scroll', imageLazyLoad)
    // or
    window.addEventListener('scroll', throttle(imageLazyLoad, 1000))
    
    11、数组扁平化
    // 方案 1
    function recursionFlat(ary = []) {
      const res = []
      ary.forEach(item => {
        if (Array.isArray(item)) {
          res.push(...recursionFlat(item))
        } else {
          res.push(item)
        }
      })
      return res
    }
    // 方案 2
    function reduceFlat(ary = []) {
      return ary.reduce((res, item) => res.concat(Array.isArray(item) ? reduceFlat(item) : item), [])
    }
    
    // 测试
    const source = [1, 2, [3, 4, [5, 6]], '7']
    console.log(recursionFlat(source))
    console.log(reduceFlat(source))
    
    12、对象扁平化
    function objectFlat(obj = {}) {
      const res = {}
      function flat(item, preKey = '') {
        Object.entries(item).forEach(([key, val]) => {
          const newKey = preKey ? `${preKey}.${key}` : key
          if (val && typeof val === 'object') {
            flat(val, newKey)
          } else {
            res[newKey] = val
          }
        })
      }
      flat(obj)
      return res
    }
    
    // 测试
    const source = { a: { b: { c: 1, d: 2 }, e: 3 }, f: { g: 2 } }
    console.log(objectFlat(source));
    

    相关文章

      网友评论

          本文标题:前端面试造火箭(手写代码)

          本文链接:https://www.haomeiwen.com/subject/qkxjuktx.html