js 手写题

197 阅读4分钟
实现new 操作符功能

实现的要点:

  1. 返回一个对象
  2. 该对象的__proto__ 指向 函数的prototype
  3. 如果有参数, 参数也需要对应到返回的对象中
function myNew(fn) {
  let obj = {}
  if (fn.prototype !== null) {
    obj.__proto__ = fn.prototype
  }
  const objWithParams = fn.apply(obj, Array.prototype.slice.call(arguments, 1))
  if (( typeof objWithParams === 'object' || typeof objWithParams === 'function') && objWithParams !== null) {
  return objWithParams
  }
  return obj
}

test

function test(name, company) {
   this.name = name
   this.company = company
}
const obj1 = myNew(test, 'test', 'test')
const obj2 = new test('test', 'test')
console.log(obj1, obj2)

result

test_new_result

debounce

触发事件后,在 n 秒内函数只能执行一次,如果触发事件后在 n 秒内又触发了事件,则会重新计算函数延执行时间

实现要点

  1. 一段时间只执行一次
  2. 一段时间内的后一次触发, 会覆盖上一次触发
  3. 返回事件函数
function debounce(fn, delay = 500, immediate) {
  let timer = undefined
  return function(){
      if (immediate) {
         fn.apply(this, arguments)
      }
      if (timer) {
         clearTimeout(timer)
      }
      timer = setTimeout(() => {
          fn.apply(this, arguments)
      }, delay)
  }
}

test

const testFn = debounce(
  () => {
    console.log('debounce', 1)
  }, 1000, false
)
const normalFn = () => {
  console.log('normal', 2)
}
const interval = setInterval(() => {
  testFn()
  normalFn()
}, 500)
setTimeout(() => {
  clearInterval(interval)
}, 2000)

result

debounce_result

throttle

一个连续操作中的处理,按照阀值时间间隔进行触发,从而实现节流

function throttle(fn, wait) {
    let timer = undefined
    return function() {
       if (!timer) {
         timer = setTimeout(() => {
          fn.apply(this, arguments)
          timer = undefined
         }, wait)
       }
    }
}

test

const testFn = throttle(() => {
  console.log('throttle', 1)
}, 500)
const interval = setInterval(() => {
  testFn()
}, 10)
setTimeout(() => {
  clearInterval(interval)
}, 2000)

result throttle_result

debounce 和throttle的区别

debounce 会覆盖上一次的触发,结果表现为一段时间内, 如果多次在delay时间间隔内触发, 只触发最后一次响应

throttle,不会覆盖上一次触发, 结果表现为一段时间内, 如果在wait时间间隔内触发, 则无响应, 超过wait时间则再次触发

深拷贝

拷贝的原因:js中对象存储在堆内存中,如果直接采用赋值的方式,只是简单的将对象的堆内存地址赋值给新变量, 这时两者的引用都指向同一个内存地址,修改任意一个,另一个都会随着改变。达不到拷贝效果。 同时,深拷贝又不同于浅拷贝, 目的是将存在多层结构对象拷贝为一个新对象, 拷贝后两者就不存在关系。 而浅拷贝只会拷贝第一层。

// 对于简单对象(非函数,正则, Date,Set, Map等)而言, 可采用json来实现.
JSON.parse(JSON.stringify(obj))
// 递归实现
function deepClone(obj) {
 let result = null
 if (typeof obj === 'object') {
    result = Array.isArray(obj) ? [] : {}
    for (let i in obj) {
       result[i] = typeof obj[i] === 'object' ? deepClone(obj[i]) : obj[i]
    }
 } else {
    result = obj
 }
 return result
}

test

const obj1 = {
    test: { innertest: '1'},
    test2: 'test2'
}
const obj3 = deepClone(obj1)

result deepClone_result

promise

参考原文: juejin.cn/post/694531…

// 三种状态, pending, fulfilled, rejected
// 状态只能由 pending -> fulfilled 或者 pending -> rejected

// Promise 对象为一个构造函数,返回一个promise实例
// Promise对象接受一个函数(executor)作为参数, 函数包含两个对象, resolve 和 reject
// resolve 函数将 promise 状态由pending 变为 fulfilled, reject 将状态由pending 变成 rejected
// promise 实例生成后, 可调用then方法。 then 接受两个回调函数作为参数, 分别指定fulfilled 和 rejected状态, 两个参数皆为可选的
// 支持异步操作, 通过一个队列来存储resolve 和reject 结果
// then 方法要链式调用那么就需要返回一个 Promise 对象
// then 方法里面 return 一个返回值作为下一个 then 方法的参数,如果是 return 一个 Promise 对象,那么就需要判断它的状态
// then 返回 promise 对象时需要判断是否为自身, 否则会造成嵌套
// 捕获错误

const PENDING = "PENDING"
const REJECTED = "REJECTED"
const FULFILLED = "FULFILLED"

function resolvePromise(promise2, value, resolve, reject) {
  if (value === promise2) {
    reject(new TypeError("Chaining cycle detected for promise #<Promise>"))
  } else if (value instanceof MyPromise) {
    value.then(resolve, reject)
  } else {
    resolve(value)
  }
}


class MyPromise {
  constructor(executor) {
    try {
      executor(this.resolve, this.reject)
    } catch (err) {
      this.reject(err)
    }
  }

  status = PENDING
  value = null
  reason = null

  resolveCallbacks = []
  rejectCallbacks = []

  resolve = (value) => {
    if (this.status === PENDING) {
      this.status = FULFILLED
      this.value = value
      while (this.resolveCallbacks.length) {
        this.resolveCallbacks.shift()(value)
      }
    }
  }

  reject = (reason) => {
    if (this.status === PENDING) {
      this.status = REJECTED
      this.reason = reason
      while (this.rejectCallbacks.length) {
        this.rejectCallbacks.shift()(reason)
      }
    }
  }

  static resolve = (parameter) => {
    if (parameter instanceof MyPromise) {
      return parameter
    }
    return new MyPromise((resolve, reject) => {
      resolve(parameter)
    })
  }

  static reject = (parameter) => {
    return new MyPromise((resolve, reject) => {
      reject(parameter)
    })
  }

  then = (onFulfilled, onRejected) => {
    const realOnFulfilled =
      typeof onFulfilled === "function" ? onFulfilled : (value) => value
    const realOnRejected =
      typeof onRejected === "function"
        ? onRejected
        : (reason) => {
            throw reason
          }
    const promise2 = new MyPromise((resolve, reject) => {
      const queueMicrotaskWithResolve = () => {
        queueMicrotask(() => {
          try {
            const resolveValue = realOnFulfilled(this.value)
            resolvePromise(promise2, resolveValue, resolve, reject)
          } catch (e) {
            reject(e)
          }
        })
      }
      const queueMicrotaskWithReject = () => {
        try {
          queueMicrotask(() => {
            const rejectValue = realOnRejected(this.reason)
            resolvePromise(promise2, rejectValue, resolve, reject)
          })
        } catch (e) {
          reject(e)
        }
      }
      if (this.status === FULFILLED) {
        queueMicrotaskWithResolve()
      } else if (this.status === REJECTED) {
        queueMicrotaskWithReject()
      } else if (this.status === PENDING) {
        this.resolveCallbacks.push(queueMicrotaskWithResolve)
        this.rejectCallbacks.push(queueMicrotaskWithReject)
      }
    })
    return promise2
  }
}

setTimeout

参考原文: github.com/sisterAn/Ja…

// 不可取方法, 会造成js死锁, 无法执行其他任务
// function mySetTimeout(fn, delay) {
//     let flag = true
//     const now = new Date;
//     while (flag) {
//         const end = new Date
//         if ( end - now > delay ) {
//             flag = false
//         }
//     }
//     return fn()
// }

const mySetTimeout = (fn, delay, ...args) => {
  const start = Date.now()
  let timer
  let now
  const loop = () => {
    timer = window.requestAnimationFrame(loop)
    now = Date.now()
    if (now - start >= delay) {
      fn.apply(this, args)
      window.cancelAnimationFrame(timer)
    }
  }
  window.requestAnimationFrame(loop)
}

function showName() {
  console.log("Hello")
}
mySetTimeout(showName, 1000)

instanceOf

通过原型链来判断一个对象的原型

function myInstanceOf(instance, Fn) {
    let obj = instance['__proto__']
    const prototype = Fn.prototype
    while(true) {
        if (obj === null || obj === undefined) {
           return false
        }
        if (obj === prototype) {
            return true
        }
        obj = obj['__proto__']
    }
}

test

myInstanceOf({name: 'test'}, Object)

result

instanceof

持续更新。。。