ES6中javascript实现函数绑定及类的事件绑定功能详解


Posted in Javascript onNovember 08, 2017

本文实例讲述了ES6中javascript实现函数绑定及类的事件绑定功能的方法。分享给大家供大家参考,具体如下:

函数绑定

箭头函数可以绑定this对象,大大减少了显式绑定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的指代是动态改变的,但是局部变量的指代却是明确的,并且,函数定义的局部变量在整个函数里面都可以用。所以,我们可以使用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就可以了。但是原生js需要提供回调函数也有它的道理,因为同一个元素的同一种事件可以绑定多个回调函数,所以你需要指出释放哪一个。

方案二:使用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的程序设计有所帮助。

Javascript 相关文章推荐
HTA版JSMin(省略修饰语若干)基于javascript语言编写
Dec 24 Javascript
JavaScript中常用的运算符小结
Jan 18 Javascript
高效率JavaScript编写技巧整理
Aug 23 Javascript
javascript中mouseover、mouseout使用详解
Jul 19 Javascript
JavaScript学习小结(7)之JS RegExp
Nov 29 Javascript
如何解决手机浏览器页面点击不跳转浏览器双击放大网页
Jul 01 Javascript
javascript中apply/call和bind的使用
Feb 15 Javascript
Vue组件选项props实例详解
Aug 18 Javascript
jquery.rotate.js实现可选抽奖次数和中奖内容的转盘抽奖代码
Aug 23 jQuery
JS/HTML5游戏常用算法之碰撞检测 包围盒检测算法详解【矩形情况】
Dec 13 Javascript
手把手15分钟搭一个企业级脚手架
Sep 16 Javascript
vue模块移动组件的实现示例
May 20 Javascript
手把手教你使用vue-cli脚手架(图文解析)
Nov 08 #Javascript
vue中实现滚动加载更多的示例
Nov 08 #Javascript
详解使用webpack打包编写一个vue-toast插件
Nov 08 #Javascript
结合mint-ui移动端下拉加载实践方法总结
Nov 08 #Javascript
详解如何使用webpack在vue项目中写jsx语法
Nov 08 #Javascript
thinkjs 文件上传功能实例代码
Nov 08 #Javascript
基于jQuery的$.getScript方法去加载javaScript文档解析
Nov 08 #jQuery
You might like
GBK的页面输出JSON格式的php函数
2010/02/16 PHP
超级实用的7个PHP代码片段分享
2012/01/05 PHP
windows7下安装php的imagick和imagemagick扩展教程
2014/07/04 PHP
/etc/php-fpm.d/www.conf 配置注意事项
2017/02/04 PHP
基于jquery封装的一个js分页
2011/11/15 Javascript
利用webqq协议使用python登录qq发消息源码参考
2013/04/08 Javascript
读取input:file的路径并显示本地图片的方法
2013/09/23 Javascript
js 高效去除数组重复元素示例代码
2013/12/19 Javascript
延时加载JavaScript代码提高速度
2015/12/27 Javascript
JS代码实现根据时间变换页面背景效果
2016/06/16 Javascript
浅谈Javascript中的12种DOM节点类型
2016/08/19 Javascript
解决bootstrap下拉菜单点击立即隐藏bug的方法
2017/06/13 Javascript
js点击时关闭该范围下拉菜单之外的菜单方法
2018/01/11 Javascript
js定义类的方法示例【ES5与ES6】
2019/07/30 Javascript
layui表格设计以及数据初始化详解
2019/10/26 Javascript
vue 实现LED数字时钟效果(开箱即用)
2019/12/08 Javascript
node.js中npm包管理工具用法分析
2020/02/14 Javascript
原生JS实现无缝轮播图片
2020/06/24 Javascript
[40:48]DOTA2上海特级锦标赛D组败者赛 Liquid VS COL第二局
2016/02/28 DOTA
让python json encode datetime类型
2010/12/28 Python
在IIS服务器上以CGI方式运行Python脚本的教程
2015/04/25 Python
Python2.7简单连接与操作MySQL的方法
2016/04/27 Python
python实现决策树ID3算法的示例代码
2018/05/30 Python
python实现雨滴下落到地面效果
2018/06/21 Python
Windows下python3.6.4安装教程
2018/07/31 Python
python反编译学习之字节码详解
2019/05/19 Python
如何使用PyCharm将代码上传到GitHub上(图文详解)
2020/04/27 Python
matplotlib部件之套索Lasso的使用
2021/02/24 Python
海蓝之谜(LA MER)澳大利亚官方商城:全球高端奢华护肤品牌
2017/10/27 全球购物
日本食品网上商店:JaponShop.com
2017/11/28 全球购物
软件测试笔试题
2012/10/25 面试题
幼儿园园长岗位职责
2013/11/26 职场文书
求职自荐信
2013/12/14 职场文书
年度考核自我鉴定
2014/02/02 职场文书
纪检部部长竞选稿
2015/11/21 职场文书
JS前端可扩展的低代码UI框架Sunmao使用详解
2022/07/23 Javascript