美文网首页
! js手写实践

! js手写实践

作者: monkeyfly36 | 来源:发表于2020-06-17 14:58 被阅读0次

    1.闭包
    2.js执行机制
    3.new发生了什么
    4.防抖和节流
    5.js原型和原型链
    6.js作用域和作用域链
    7.深拷贝、浅拷贝
    8.Map Set

    ***************************** 升阶 ******************************
    1.手写Promise
    2.手写防抖(Debounce)和节流(Throttle)
    3.手写深拷贝
    4.手写EventEmitter
    5.手写柯里化(2种)

    ***************************** 基础 ******************************
    2-1.手写compose
    2-2.手写reduce
    2-3.手写instanceof ?
    2-4.手写new?
    2-4.手写aplly call bind?--作用: 指定上下文
    2-4.手写继承(原型, class)?

    一.手写Promise

    class Kromise {
      constructor(executor){
        this.state = 'pendding'
        this.value = undefined
        this.reason = undefined
        this.onResolvedStack = []
        this.onRejectedStack = []
        let resolve = value => {
          this.state = 'fulfilled'
          this.value = value
        }
        let reject = reason => {
          this.state = 'rejected'
          this.reason = reason
        }
        executor(resolve, reject)
      }
      static resolve(value) {
        return new Kromise((resFn, rejFn) => {
          resFn(value)
        })
      }
      static reject(reason) {
        return new Kromise((resFn, rejFn) => {
          rejFn(reason)
        })
      }
      static all(promises) {
        return new Kromise((resFn, rejFn) => {
          let reslovedResult = []
          let count = 0
          let state = false // 只要有一条报错,则返回这一条 ?? 使用state不是最优
          for(let i = 0; i < promises.length; i++) {
            if(!state) {
              promises[i].then(res => { // 所有数据都resolve返回数组数据
                count ++
                reslovedResult[i] = res
                if (count === promises.length) {
                 resFn(reslovedResult)
                }
              }, err => { // 有一条数据reject,返回该数据
                state = true
                rejFn(err)
              }) 
            }
          }
        })
      }
      static allSettled(promises) {
        return new Kromise((resFn, rejFn) => {
          let reslovedResult = []
          for(let i = 0; i < promises.length; i++) {
            promises[i].then(res => reslovedResult[i] = res, err => reslovedResult[i] = err)
          }
          resFn(reslovedResult)
        })
      }
      static race(promises) { // 只要一个成功或失败,就返回该数据
        return new Kromise((resFn, rejFn) => {
          for(let i = 0; i < promises.length; i++) {
            promises[i].then(resFn, rejFn)
            return
          }
        })
      }
      then(resFn, rejFn) {
        return new Kromise((resolve, reject) => {
          if(this.state === 'fulfilled') {
            resFn(this.value)
          } else if(this.state === 'rejected') {
            rejFn(this.reason)
          } else {
            this.onResolvedStack.push(() => resFn(this.value))
            this.onRejectedStack.push(() => rejFn(this.reason))
          }
        })
      }
    }
    // ==================================================
    new Kromise(res => {
      res(1)
      new Kromise(res1 => res1(2)).then(val => console.log(val))
    }).then(val => console.log(val))
    
    Kromise.reject(22).then(res => {}, err => console.log(err))
    
    const p1 = Kromise.resolve(11)
    const p2 = Kromise.reject(22)
    const p3 = Kromise.reject(33)
    Kromise.race([p1,p2,p3]).then(res => console.log(res), err => console.log(err))
    Kromise.all([p1,p2,p3]).then(res => console.log(res), err => console.log(err))
    Kromise.allSettled([p1,p2,p3]).then(res => console.log(res), err => console.log(err))
    

    二.手写防抖(Debouncing)和节流(Throttling)

    const p = function(val,val2){console.log(val,val2)}
    // 防抖---一次动作(打印)完成后,(200ms内不会生效, 本质第一次操作也是在200ms后执行)
    function debounce(fn, delay) {
      let timer
      return function (...arg) {
        if (timer) clearTimeout(timer) 
        setTimeout(() => fn.call(this, arg), delay)
      }
    }
    // ===================================================
    let d1 = throttle(p, 200)
    d1(11,22)
    d1(33)
    setTimeout(() => d1(44), 100)
    setTimeout(() => d1(55), 150)
    setTimeout(() => d1(66), 150)
    setTimeout(() => d1(77), 500)
    
    // 节流---一段时间内仅执行一次操作
    function throttle(fn, delay) {
      let lastTime = 0
      return function(...arg) {
        let nowTime = new Date().getTime()
        if(nowTime - lastTime > delay) {
          // fn.call(this, arg)
          fn.apply(this, arg)
        }
        lastTime = nowTime
      }
    }
    // ==================================================
    let p1 = throttle(p, 1000)
    p1(1,2)
    p1(2)
    setTimeout(() => p1(3), 500)
    setTimeout(() => p1(4), 2000)
    

    三.手写深拷贝

    function deepClone(obj) {
      let target = Array.isArray(obj) ? [] : {} // 判断是数组还是对象
      for(let i in obj) {
        if(Object.prototype.hasOwnProperty.call(obj, i)) { // 判断是否是自身属性
          if(typeof obj[i] === 'object') { // 判断子值类型
            deepClone(obj[i])
          } else {
            target[i] = obj[i]
          }
        }
      }
      return target
    }
    // 对象
    var p = { name: 'xiaoming' }
    var p1 = deepClone(p)
    p1.name = 'xiaowen'
    console.log(p, p1)
    // 数组
    var p = [1,2,3,4,5]
    var p1 = deepClone(p)
    p1[1] = 1
    console.log(p, p1)
    

    四. 手写EventEmitter

    // 最简版, 只考虑注册一次,若考虑多次注册就用数组
    class KventEmitter {
      constructor() {
        this.handler = new Map()
      }
      on(eventName, fn) {
        this.handler.set(eventName, fn)
      }
      emit(eventName, param) {
        const fn = this.handler.get(eventName)
        fn(param)
      }
    }
    // ========================================
    const event = new KventEmitter()
    event.on('some_event', num =>  { 
      console.log('some_event 事件触发:'+num)
    })
    let num = 0
    setInterval(() =>  { 
      event.emit('some_event' , num ++ )
    }, 1000)
    

    五. 手写柯里化(返回值是函数)
    定义:通过把一个多参函数转换为一系列嵌套函数,每个函数依次接受一个参数
    本质: 中间件处理数据

    // A.
    function currying(fn) {
      let args = [] // 闭包变量
      const cb = function(...arg) {
        if(arg.length === 0) return fn.apply(this, args) // 执行请求
        args = [...args, ...arg] //收集参数
        return cb // 返回函数供传参
      }
      return cb // 第一次函数
    }
    const add = (...arg) => arg.reduce((a,b) => a+b)
    const curry = currying(add)
    // ===================================================
    console.log(curry(1)(2,3)())
    
    
    // B.理解
    function curry(fn) {
      // 保存预置参数, 去除fn参数
      const presetArgs = [].slice.call(arguments, 1)
      // 返回一个新函数
      function curried (...arg) {
        // 新函数调用时会继续传参
        const allArgs = [...presetArgs, ...arg]
        return curry.call(null, fn, ...allArgs)
      }
      // 重写toString
      curried.toString = function() {
        return fn.apply(null, presetArgs)
      }
      return curried
    }
    
    function dynamicAdd() {
      return [...arguments].reduce((prev, curr) => {
        return prev + curr
      }, 0)
    }
    var add = curry(dynamicAdd);
    console.log(add(1)(2,3).toString()) // 10
    
    // B.精简
    const add = (...args) => {
      // 精髓1: 将args作为闭包变量, 收集所有参数
      let _adder = (..._args) => {
        args = [...args, ..._args]
        return _adder
      }
      // 精髓2: 利用toString隐式转换的特性,当最后执行时隐式转换,并计算最终的值返回
      _adder.toString = () => args.reduce((a, b) => a + b)
      return _adder
    }
    // ===================================================
    console.log(add(2,3)(1)(2).toString())
    

    2-1. 手写compose

    
    

    2-2. 手写reduce

    Array.prototype.keduce = function (fn, preV) {
      preV = preV ?? this[0]
      for(let i = 1; i < this.length; i++) {
        preV = fn(preV, this[i]) // preV是闭包中保存参数变量
      }
      return preV
    }
    // ===================================================
    console.log([1,2,3,4].keduce((a,b) => a*b))
    

    相关文章

      网友评论

          本文标题:! js手写实践

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