ES6 JavaScript 函数绑定与类事件绑定实战指南

作者:半夏时间:2026-10-22 09:47:39浏览:105

本文详解ES6中函数绑定运算符(::)的用法及在类中处理事件绑定的多种方案。针对事件回调this指向问题,对比了局部变量保存、bind()绑定及Symbol私有方法等实现方式,帮助开发者清晰管理类内部状态并实现安全的私有事件处理。

本文介绍在ES6环境下,利用JavaScript实现函数绑定以及类内部事件绑定的具体方法,供读者参考学习。

函数绑定运算符详解

函数绑定

箭头函数能够自动绑定this对象,从而大幅减少了使用call、apply或bind进行显式绑定的需求。然而,箭头函数并非适用于所有场景,因此ES7提案中引入了“函数绑定”(function bind)运算符,旨在替代传统的call、apply和bind调用。尽管该语法目前仍为ES7提案,但Babel转码器已提供支持。

函数绑定运算符由并排的两个双冒号(::)组成。双冒号左侧指定一个对象,右侧指定一个函数。该运算符会自动将左侧对象作为上下文环境(即this对象)绑定到右侧函数上。


foo::bar;
// 等同于
bar.bind(foo);
foo::bar(...arguments);
// 等同于
bar.apply(foo, arguments);
const hasOwnProperty = Object.prototype.hasOwnProperty;
function hasOwn(obj, key) {
return obj::hasOwnProperty(key);
}

若双冒号左侧为空,而右侧为某对象的方法,则等同于将该方法绑定至该对象本身。


var method = obj::obj.foo;
// 等同于
var method = ::obj.foo;
let log = ::console.log;
// 等同于
var log = console.log.bind(console);

由于双冒号运算符返回的仍是原对象,因此支持链式调用写法。


// 例一
import { map, takeWhile, forEach } from "iterlib";
getPlayers()
::map(x => x.character())
::takeWhile(x => x.strength > 100)
::forEach(x => console.log(x));
// 例二
let { find, html } = jake;
document.querySelectorAll("div.myClass")
::find("p")
::html("hahaha");

类中事件绑定解决方案

类中事件绑定

概述

ES6引入了类概念,极大促进了模块化开发。在类内部绑定事件有助于保持代码结构清晰,并能访问类的变量与方法。然而,由于事件回调函数并非由类的实例直接触发,导致回调内部无法直接访问类的this变量。此外,为避免调用者直接调用回调函数,我们通常不希望其对外暴露。

简而言之,我们的需求包括:

1. 事件回调函数需能访问类的this变量 2. 事件回调函数不应被直接调用

访问类this的两种方案

如何访问类的this

方案一:将类的this保存为局部变量

this的指向是动态变化的,而局部变量的指向明确且在整个函数作用域内有效。因此,我们可以使用let that = this来保存类的this变量。


class A{
  //绑定事件的方法
  bindEvent(){
   let that = this;
   this.button1.on('click',function(e){
      this.addClass('on'); //this指代所点的元素
      that.doSomething(); //that指向类的this
   })
  }
  doSomething(){
   //事件处理函数
  }
  //解绑事件
  unBindEvent(){
   this.button1.off();
  }
}

此方法主要适用于jQuery场景,因为jQuery在解绑事件时无需指定回调函数,直接调用off即可。但在原生JavaScript中,解绑事件需提供回调函数,这是合理的,因为同一元素的同一事件可绑定多个回调,必须明确指定解除哪一个。

方案二:使用bind()改变this指向

假设存在类A,需在A中添加mousemove事件,根据需求编写如下代码:


class A{
  //添加事件
  addEvent(){
    document.addEventListener( 'mousemove', onMouseMove, false );
  }
  //添加事件
  removeEvent(){
    document.removeEventListener( 'mousemove', onMouseMove , false );
  }
}
//事件回调函数中
function onMouseMove(event){
  console.log(this);  //#document
}

然而,上述方式无法获取类的this。onMouseMove中的this将指向document。因为事件绑定在document上,自然由document触发事件并调用onMouseMove进行处理,导致onMouseMove中的this指向document。

更合理的做法是:使用bind()函数改变onMouseMove中this的指向,同时将事件回调函数移至类外部:


class A{
  //添加事件
  addEvent(){
    document.addEventListener( 'mousemove', onMouseMove.bind(this), false );
  }
  //添加事件
  removeEvent(){
    document.removeEventListener( 'mousemove', onMouseMove.bind(this) , false );
  }
}
//事件回调函数中
function onMouseMove(event){
  console.log(this);
}

但此方式仍存在一个问题:事件无法移除!因为this.bind()每次调用都会返回一个新的函数实例,导致:


document.addEventListener( 'mousemove', onMouseMove.bind(this), false );

与


document.removeEventListener( 'mousemove', onMouseMove.bind(this), false );

两者的第二个参数并不相同。

正确的做法是:将bind()的结果保存到一个变量中:


class A{
  constructor(){
    this._onMouseMove = onMouseMove.bind(this);  //看这里
  }
  //添加事件
  addEvent(){
    document.addEventListener( 'mousemove', this._onMouseMove , false );
  }
  //添加事件
  removeEvent(){
    document.removeEventListener( 'mousemove', this._onMouseMove , false );
  }
}
//事件回调函数中
function onMouseMove(event){
  console.log(this);
}

定义私有事件回调函数

如何定义私有的事件回调函数

在Java中,不对外暴露的方法可定义为私有方法,但ES6未提供私有方法机制,需通过其他方式模拟。事件回调函数较为特殊,除定义外还需移除,这增加了额外复杂性。不过仍有解决思路:

使用Symbol变量来定义


const _onMouseMove = Symbol("_onMouseMove");
class A{
  constructor(){
    this[_onMouseMove] = onMouseMove.bind(this);
  }
  //添加事件
  addEvent(){
    document.addEventListener( 'mousemove', this[_onMouseMove] , false );
  }
  //添加事件
  removeEvent(){
    document.removeEventListener( 'mousemove', this[_onMouseMove] , false );
  }
}
//事件回调函数中
function onMouseMove(event){
  console.log(this);
}

Symbol("_onMouseMove")会产生一个唯一值,该值在对象创建时生成。调用者无法在编码时预知此值,因此无法调用以此值命名的方法,从而实现私有方法的定义。

更多相关内容可查看本站专题:《ECMAScript6(ES6)入门教程》、《JavaScript数组操作技巧总结》、《JavaScript字符与字符串操作技巧总结》、《JavaScript数据结构与算法技巧总结》、《JavaScript错误与调试技巧总结》及《javascript面向对象入门教程》

希望本文所述内容对大家基于ECMAScript的程序设计有所帮助。

作者最新文章