用ES6写jquery语法时的this绑定问题

在es6里使用jquery时,习惯用es5中直接获取dom元素,如$('selector'), 绑定clickhover等事件; 若你也追求潮流,在选择器事件中使用箭头函数来定义,并用到$(this)的话, 那一定像我一样遇到了小小的困扰吧。

问题重现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
$('[data-action="imgQRCodeCtrl"]').hover(() => {
!Base.browser.mobile && $(this).find('.qr-code').css({
'height': ( $(this).find('.img').height() + 2 ) + 'px',
'opacity': 1,
'z-index': 1
});
}, () => {
$(this).find('.qr-code').css({'opacity': 0, 'z-index': -1});
})
.click(() => {
if(Base.browser.mobile){
location.href = $(this).data('href');
}
});

乍一看,这段代码没什么问题,逻辑也很简单,就是在所有属性有data-action="imgQRCodeCtrl"的列表元素上绑定hoverclick事件,实现pc端hover时能够使内部的一个块显示或隐藏,移动端点击时获取当前属性上的href并进行跳转;

当然so easy,在过去的es5上按这种逻辑写完全么有问题;but ES6里解析后,会重新定义this,

如下是真实浏览器上加载的解析后的代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
var $ = __webpack_require__("./node_modules/jquery/dist/jquery.js");
$(function () {
var _this = this;
//....
$('[data-action="imgQRCodeCtrl"]').hover(function () {
!_base2.default.browser.mobile && $(_this).find('.qr-code').css({
'height': $(_this).find('.img').height() + 2 + 'px',
'opacity': 1,
'z-index': 1
});
}, function () {
$(_this).find('.qr-code').css({ 'opacity': 0, 'z-index': -1 });
}).click(function () {
if(_base2.default.browser.mobile){
location.href = $(_this).data('href');
}
});
});

this发生了什么!

即便没有真实运行这段代码,有点基础的,肯定能预见到页面上发生了什么。。。

原因

This has nothing to do with Traceur and turning something off, this is simply how ES6 works. It’s the specific functionality you’re asking for by using => instead of function () { }.

If you want to write ES6, you need to write ES6 all the time, you can’t switch in and out of it on certain lines of code, and you definitely cannot suppress or alter the way => works. Even if you could, you would just wind up with some bizarre version of JavaScript that only you understand and which would never work correctly outside of your customized Traceur, which definitely isn’t the point of Traceur.

我理解的大意是说 “ES6的工作方式就是这样,跟编译无关,与写法有关;若想用ES6,最好全用ES6的写法,不能改变它原本的定义…”(英文不好,欢迎大神指正 >_< )

看到这儿,吓得我赶紧查了查文档

原来箭头函数,不绑定自己的this,这一下就说通了;好好看看下面这句吧,不遇到问题时永远记忆模糊,哈哈

箭头函数表达式的语法比函数表达式更短,并且不绑定自己的this,arguments,super或 new.target。这些函数表达式最适合用于非方法函数,并且它们不能用作构造函数。

问题解决

  • 方法一:弄清除原理后,就很简单了,绑定this的函数不使用箭头函数不就行了,如下试验果然ok:
1
2
3
4
5
6
7
$('[data-action="imgQRCodeCtrl"]')
//...
.click(function() {
if (Base.browser.mobile) {
location.href = $(this).data('href');
}
});
  • 方法二:不使用this, 使用event.currentTarget
1
2
3
4
5
6
7
$('[data-action="imgQRCodeCtrl"]')
//...
.click((event) => {
if (Base.browser.mobile) {
location.href = $(event.currentTarget).data('href');
}
});
  • 方法三:利用each函数循环绑定到每一个小元素上,如此就不需要this
1
2
3
4
5
6
7
8
$('[data-action="imgQRCodeCtrl"]').each((index, element) => {
let _this = element;
$(_this).click(() => {
if (Base.browser.mobile) {
location.href = $(_this).data('href');
}
});
});

总结

以上的解决方法都能得到想要的效果,不过还是提议使用 方法一 来解决,更简单直白,在维护和团队合作上会起很大作用,因为没人愿意读别人写的拐了十八个弯弯的代码

这个问题挺小的,可能某些大牛看到后会鄙视我,不过喜欢这种遇到问题自己一步步解决并完美解释清楚的感觉,还在路上,有问题欢迎交流指正,以后还会多多以这种方式总结下。。。

参考链接
https://stackoverflow.com/questions/27670401/using-jquery-this-with-es6-arrow-functions-lexical-this-binding

ES6