持续更新…
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 主协程的工作执行完毕