实现执行多次事件只触发一次的once效果

615 阅读2分钟

开启掘金成长之旅!这是我参与「掘金日新计划 · 2 月更文挑战」的第 8 天,点击查看活动详情

在 Vue 中 .once 是事件的修饰符之一,表示事件只会触发一次

<button @click.once="fn" >确定</button>

那么如果要使用 js 来实现和 .once 相同效果,核心思路其实是使用闭包

执行多次,事件只会触发一次

实现思路:

  • 设定一个标记,初始为 false,当触发事件,标记值如果为 false,将其值设为 true,执行对应事件;标记值如果为 true,不执行。
  • 函数的执行,使用 apply() 来实现

如下代码就应用了高级函数的写法(高阶函数,是指以函数作为参数或以函数作为返回值的函数):

var once = fn => {
    let done = false
    return function () {
        if(!done) {
            done = true
            fn.apply(this, arguments)
        }
    }
}

var doSomething = once(() => {
    console.log('hello')
})

doSomething()
doSomething()
doSomething()
doSomething()
doSomething()
doSomething()

闭包中的核心代码,也可以简写成:

return done ? undefined : (( done = true), fn.apply(this, arguments))

执行这段代码,运行后可以看到多次执行函数, hello 只输出了一次,效果如下:

image.png

关于 apply()

在 js 中,apply、call、bind 都可以用来改变 this 的指向。它可以劫持另外一个对象的方法,继承另外一个对象的属性。

call 和 apply 的区别只有参数的形式不同

  • fn.call(this, arg1, arg2);
  • fn.apply(this, [arg1, arg2]); // 或 fn.apply(this, arguments)

使用场景的选择:

  • 函数参数确定的时候,使用 call
  • 如果函数的参数不固定,使用 apply (fn.apply(this, arguments) 是参数不固定的写法,arguments 是一个数组,可遍历所有的参数)

除此之外,如果 call 或 apply 方法的第一参数是 null, this 指向的是 window

apply / call 与 bind 的区别:

  • apply / call 会立即执行函数
  • bind 不会立即执行函数

如果改变上下文环境之后不需要立即执行,而是回调执行的时候,使用 bind() 方法。同时,使用 bind() 也可以实现立即执行:

fn.call(obj) 
// 等价于
fn.bind(obj)()

其它

函数调用的三种方式,写法如下:

  • obj.myFunc();
  • myFunc.call(obj,arg);
  • myFunc.apply(obj,[arg1,arg2..]);