前端日常tips记录

持续更新…

div嵌套的事件拦截

网上能查到的大多是拦截冒泡,但下面的例子是拦截事件往内部传递,在外部就拦截掉

<div class="outer" @click.capture="outerClick($event)">
    <div class="inner" @click="innerClick($event)"></div>
</div>

outerClick(event) {
  console.log('outer')
  event.stopImmediatePropagation()
},

innerClick (event) {
  console.log('inner')
}

或者,通过addEventListener来操作

let elWrapper = document.createElement('div')
elWrapper.addEventListener('click', function (e) {
  console.log('click: ' + e)
  e.stopImmediatePropagation()
}, true)

最简单的

<div @click.stop.capture="onClick"></div>

有篇非常好的文章:
https://www.cnblogs.com/zhuzhenwei918/p/6139880.html

理解KOA2的use

const mw1 = async (ctx, next) => {
 console.log('mw1')
  if (next) {
    await next()
  }
  console.log('mw1 end')
}

const mw2 = async (ctx, next) => {
  console.log('mw2')
  if (next) {
    await next()
  }
  console.log('mw2 end')
}

const mw3 = async (ctx, next) => {
  console.log('mw3')
  if (next) {
    await next()
  }
  console.log('mw3 end')
}

const mw4 = async (ctx, next) => {
  console.log('mw4')
  if (next) {
    await next()
  }
  console.log('mw4 end')
}

const createNext = (middleware, next) => {
  return async (ctx) => {
    await middleware(ctx, next)
  }
}

const middlewares = []
middlewares.push(mw1)
middlewares.push(mw2)
middlewares.push(mw3)
middlewares.push(mw4)

const comboMiddlewares = middlewares.reduceRight((next, mw) => {
  return createNext(mw, next)
})

const ctx = ''
comboMiddlewares(ctx) // 相当于 mw1(ctx, createNext(mw2, createNext(mw3, mw4))) 因为最后一个mw4没有next了 所以不需要createNext

输出:
mw1
mw2
mw3
mw4
mw4 end
mw3 end
mw2 end
mw1 end

高阶函数

// y=f(x) z=g(y) => z = g(f(x))
// 需要折腾的函数
const f = (x) => {
  return 'f' + ' ' + x
}

// 包裹函数
const g = (y) => {
  return 'g' + ' ' + y
}

// 用一个高阶函数,返回一个入参和f相同的函数,高阶函数的入参为f和包裹的函数
const compose = (f, g) => (x) => {
  return g(f(x))
}

const comp = compose(f, g)
const r = comp('1')
console.log(r)

一个更业务的case

const add = (val) => {
  return val + ' add'
}

const toUpper = (val) => {
  return val.toUpperCase()
}

const double = (val) => {
  val += val
  return val
}

const compose = (add, toUpper, double) => val => {
  if (add) {
    val = add(val)
  }
  if (toUpper) {
    val = toUpper(val)
  }
  if (double) {
    val = double(val)
  }

  return val
}

const comp = compose(add, toUpper)
const r = comp('x')
console.log(r)

把compose函数封装成一般式

const add = (val) => {
  return val + ' add'
}

const toUpper = (val) => {
  return val.toUpperCase()
}

const double = (val) => {
  val += val
  return val
}

const compose = (...fns) => {
  const length = fns.length
  if (length === 0) {
    return 
  }

  let count = 0
  let result

  return function f (...args) {
    result = fns[count++].apply(this, args)
    if (count < length) {
      // eslint-disable-next-line no-useless-call
      result = f.call(null, result)
    }

    return result
  }
}

const comp = compose(add, toUpper, double)
console.log(comp('x'))

输出X ADDX ADD

Curry

柯里化是一种函数的转换,它是指将一个函数从可调用的 f(a, b, c) 转换为可调用的 f(a)(b)©。
柯里化不会调用函数。它只是对函数进行转换。

const curry = (f) => a => b => c => f(a, b, c)
const sum = (a, b, c) => {
  return a + b + c
}

const curriedSum = curry(sum)
console.log(curriedSum(1)(2)(3))

通用柯里化实现

const currying = (func) => {
  return function curried (...args) {
    if (args.length >= func.length) {
      return func.apply(this, args)
    } else {
      return pass (...args2) => {
        return curried.apply(this, args.concat(args2))
      }
    }
  }
}

const curriedSum = currying(sum)
console.log(curriedSum(1)(2)(3))

对于调用 curried(1)(2)(3):

第一个调用 curried(1) 将 1 保存在词法环境中,然后返回一个包装器 pass。

包装器 pass 被调用,参数为 (2):它会获取之前的参数 (1),将它与得到的 (2) 连在一起,并一起调用 curried(1, 2)。由于参数数量仍小于 3,curry 函数依然会返回 pass。

包装器 pass 再次被调用,参数为 (3),在接下来的调用中,pass(3) 会获取之前的参数 (1, 2) 并将 3 与之合并,执行调用 curried(1, 2, 3) — 最终有 3 个参数,它们被传入最原始的函数中。

手写一个clone函数

function copy(src) {
  if (typeof src === 'object') {
    if (!src) {
      // null
      return src
    } else {
      const obj = src.constructor()
      Object.keys(src).forEach(key => {
        obj[key] = copy(src[key])
      })
      return obj
    }
  } else {
    return src
  }
}

尝试写一个js的BlockingQueue

没有任务的时候,queue会阻塞,有任务就按顺序执行

class BlockingQueue {
  constructor () {
    this.tasks = []
    this.running = false
  }

  async start() {
    if (this.running) {
      return
    }

    this.running = true
    for (;;) {
      const task = this.tasks.shift()
      if (!task) {
        console.log('waiting...')
        await new Promise(resolve => {
          this.resolveRef = resolve
        })
      } else {
        await task()
      }

      if (!this.running) {
        break
      }
    }
  }

  add (task) {
    this.tasks.push(task)
    if (this.resolveRef) {
      this.resolveRef()
      this.resolveRef = null
    }
  }

  stop () {
    this.running = false
  }
}

const blockingQueue = new BlockingQueue()
blockingQueue.start()

blockingQueue.add(() => new Promise(resolve => {
  setTimeout(() => {
    console.log('task 1 finished')
    resolve()
  }, 3000)
}))
blockingQueue.add(() => new Promise(resolve => {
  setTimeout(() => {
    console.log('task 2 finished')
    resolve()
  }, 1000)
}))
blockingQueue.add(() => console.log('xxx'))

再写个一次多任务并行的版本

class BlockingQueue {
  constructor () {
    this.queue = []
    this.running = false
    this.resolveF = null
  }

  async start () {
    if (this.running) {
      return
    }

    for (;;) {
      const tasks = this.queue.shift()
      if (!tasks) {
        console.log('waiting')
        await new Promise(resolve => {
          this.resolveF = resolve
        })
      } else {
        await this.invokeTasks(tasks)
      }
    }
  }

  async invokeTasks (tasks) {
    await new Promise(resolve => {
      let cbCount = 0
      for (let task of tasks) {
        const cb = () => {
          cbCount++
          if (cbCount === tasks.length) {
            resolve()
          }
        }

        task(cb)
      }
    })
  }

  add (tasks) {
    if (!tasks) {
      return
    }

    if (!Array.isArray(tasks)) {
      tasks = [tasks]
    }

    if (tasks.length > 2) {
      throw new Error('error')
    }

    this.queue.push(tasks)
    if (this.resolveF) {
      this.resolveF()
    }
  }
}

const timeout = 3000

const blockingQueue = new BlockingQueue()
blockingQueue.start()
const t1 = (cb) => {
  setTimeout(() => {
    console.log('p1')
    cb()
  }, timeout)
}

const t2 = (cb) => {
  setTimeout(() => {
    console.log('p2')
    cb()
  }, timeout)
}

const t3 = (cb) => {
  console.log('p3')
  cb()
}

blockingQueue.add([t1, t2])
blockingQueue.add(t3)

一个例子理解宏任务,微任务,协程

async function foo() { 
  console.log('foo') // 3 先执行foo函数,创建一个promise_传递给主协程,并添加到微任务队列
}

async function bar() { 
  console.log('bar start') // 2 setTimeout定义在下一个宏任务,先执行bar(),执行权交给bar协程
  await foo() 
  console.log('bar end') // 6 检查微任务,第一个微任务执行,控制权交给bar函数
}

console.log('script start') // 1 程序开始

setTimeout(function () { 
  console.log('setTimeout') // 8 进入下一个宏任务
}, 0)

bar()

new Promise(function (resolve) { 
  console.log('promise executor') // 4 执行executor,resolve添加到微任务队列
  resolve()
}).then(function () { 
  console.log('promise then') // 7 执行第二个微任务
})

console.log('script end') // 5 主协程的工作执行完毕
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值