手写某些函数

124 阅读2分钟

「本文已参与好文召集令活动,点击查看:后端、大前端双赛道投稿,2万元奖池等你挑战!

大家在面试当中经常会被问到“你了解XXX的原理吗?”“你可以用自己的思路实现XXX吗?”此篇文章旨在使用自己的思路实现某些函数,希望对大家有些许的帮助。

数组的 map 方法

数组的 map 方法实现的是创建一个新数组,其结果是该数组中的每个元素是调用一次提供的函数后的返回值。

Array.prototype.map = function(fn){
  let result = []
  for(let i; i<this.length; i++){
    if(i in this){
        result.push(fn(this[i], i, this))
    }
  }
  return result
}

Promise

class Promise {
  constructor(executor) {
    // 初始化状态为 pending
    this.state = 'pending'
    // 成功的值
    this.value = undefined
    // 失败的原因
    this.reason = undefined
    // 成功存放的数组
    this.onResolvedCallbacks = []
    // 失败存放的数组
    this.onRejectedCallbacks = []

    let resolve = value => {
      if (this.state === 'pending') {
        // resolve 调用成功后,将状态改变为 fulfilled
        this.state = 'fulfilled'
        // 储存成功的值
        this.value = value
        // 一旦 resolve 执行,调用成功数组的函数
        this.onResolvedCallbacks.forEach(fn => fn())
      }
    }

    let reject = reason => {
      if (this.state === 'pending') {
        // reject 调用后,状态改变为 rejected
        this.state = 'rejected'
        // 储存失败的原因
        this.reason = reason
        // 一旦r eject 执行, 调用失败数组的函数
        this.onRejectedCallbacks.forEach(fn => fn())
      }
    }

    try {
      executor(resolve, reject)
    } catch (err) {
      reject(err)
    }
  }

  then(onFulfilled, onRejected) {
    if (this.state === 'fulfilled') {
      onFulfilled(this.value)
    }
    if (this.state === 'rejected') {
      onRejected(this.reason)
    }
    // 当状态为 pending 的时候
    if (this.state === 'pending') {
      // onFulfilled 的值传入成功数组
      this.onResolvedCallbacks.push(() => {
        onFulfilled(this.value)
      })
      // onRejected 的值传入失败数组
      this.onRejectedCallbacks.push(() => {
        onRejected(this.reason)
      })
    }
  }
}

深拷贝

function deepClone(obj, hash = new WeakMap()) {
  if (obj instanceof RegExp) return new RegExp(obj)
  if (obj instanceof Date) return new Date(obj)
  if (obj === null || typeof obj !== 'object') {
    return obj
  }
  if (hash.has(obj)) {
    return hash.get(obj)
  }
  let t = new obj.constructor()
  hash.set(obj, t)
  for (let key in obj) {
    if (obj.hasOwnProperty(key)) {
      t[key] = deepClone(obj[key], hash)
    }
  }
  return t
}

call/apply

Function.prototype.call = function() {
    let [thisArg, ...args] = [...arguments];
    if (!thisArg) {
        //context为null或者是undefined
        thisArg = typeof window === 'undefined' ? global : window;
    }
    //this的指向的是当前函数 func (func.call)
    thisArg.func = this;
    //执行函数
    let result = thisArg.func(...args);
    delete thisArg.func; //thisArg上并没有 func 属性,因此需要移除
    return result;
}

Function.prototype.apply = function(thisArg, rest) {
    let result; //函数返回结果
    if (!thisArg) {
        //context为null或者是undefined
        thisArg = typeof window === 'undefined' ? global : window;
    }
    //this的指向的是当前函数 func (func.call)
    thisArg.func = this;
    if(!rest) {
        //第二个参数为 null / undefined 
        result = thisArg.func();
    }else {
        result = thisArg.func(...rest);
    }
    delete thisArg.func; //thisArg上并没有 func 属性,因此需要移除
    return result;
}

防抖/节流

// 节流(一段时间执行一次之后,就不执行第二次)
function throttle(fn, delay) {
  let canUse = true
  return function() {
    if (canUse) {
      fn.apply(this, arguments)
      canUse = false
      setTimeout(() => (canUse = true), delay)
    }
  }
}

const throttled = throttle(() => console.log('hi'))
throttled()

// 防抖(一段时间会等,然后带着一起做了)
function debounce(fn, delay) {
  let timerId = null
  return function() {
    const context = this
    if (timerId) {
      window.clearTimeout(timerId)
    }
    timerId = setTimeout(() => {
      fn.apply(context, arguments)
      timerId = null
    }, delay)
  }
}
const debounced = debounce(() => console.log('hi'))
debounced()

AJAX

var request = new XMLHttpRequest()
request.open('GET', '/a/b/c', true)
request.onreadystatechange = function() {
  if (request.readyState === 4 && request.status === 200) {
    console.log(request.responseText)
  }
}
request.send('name=abc&age=20')

最终用 TS 实现的 EventHub

class EventHub {
  private cache: { [key: string]: Array<(data: unknown) => void> } = {}  

  on(eventName: string, fn: (data: unknown) => void) {
    this.cache[eventName] = this.cache[eventName] || []
    this.cache[eventName].push(fn)
  }
  emit(eventName: string, data?: unknown) {
    (this.cache[eventName] || []).forEach(fn => fn(data))
  }

  off(eventName: string, fn: (data: unknown) => void) {
    this.cache[eventName] = this.cache[eventName] || []
    let index = indexOf(this.cache[eventName], fn)
    if (index === -1) return
    this.cache[eventName].splice(index, 1)
  }
}

export default EventHub;

/**
 * 帮助函数 indexOf
 * @param arr 
 * @param item 
 */
function indexOf(arr, item) {
  if (arr === undefined) return -1
  let index = -1
  for (let i = 0; i < arr.length; i++) {
    if (arr[i] === item) {
      index = i
      break
    }
  }
  return index
}

最后说一句

如果这篇文章对您有所帮助,或者有所启发的话,帮忙点赞关注一下,您的支持是我坚持写作最大的动力,多谢支持。