除了 ES6中 的箭头函数,ES5 中如何修正 setTimeout(),$.ajax 等等中的”this”指向不正确。
假设当前页面上有一个 ID 为"btn_demo"
的按钮,我想点击之后三秒改变背景色,第一直觉可能会写出以下代码:
1 2 3 4 5 |
$("#btn_demo").on("click",function(){ setTimeout(function(){ this.style.backgroundColor="blue"; },3000); }); |
结果在 chrome 中提示Uncaught TypeError: Cannot set property 'backgroundColor' of undefined
,原因是在 setTimeout() 中,this 指向了window (全局)对象,而不是当前触发事件的对象。解决这个问题,可以显式传入外层 this:
1 2 3 4 5 6 |
$("#btn_demo").on("click",function(){ var _this=this; setTimeout(function(){ _this.style.backgroundColor="blue"; },3000); }); |
或者使用 ES6 中的箭头函数,原理与上面的一致:
1 2 3 4 5 |
$("#btn_demo").on("click",function(){ setTimeout(()=>{ this.style.backgroundColor="blue"; },3000); }); |
但是,ES5 已经为我们提供了一种更为优雅与健壮,并且适用性更强的方法:
1 2 3 4 5 |
$("#btn_demo").on("click",function(){ setTimeout(function(){ this.style.backgroundColor="blue"; }.bind(this),3000); }); |
比如在类的实例方法中,使用第bind()
方法来修改 this 的指向:
1 2 3 4 5 6 7 8 9 10 |
function Demo(name) { this.name = name; } Demo.prototype.sayName = function() { setTimeout(function() { console.log(this.name); }.bind(this), 1000); } var demo = new Demo("lai"); demo.sayName(); |
Function.prototype.bind()
第一次见到这个方法,是在 Reactjs 中:有这样一段代码,为了解决 $.ajax 中 this 指向 $.ajax 对象的问题:
1 2 3 4 5 6 7 8 9 10 11 |
$.ajax({ url: this.props.url, dataType: 'json', cache: false, success: function(data) { this.setState({data: data}); }.bind(this), error: function(xhr, status, err) { console.error(this.props.url, status, err.toString()); }.bind(this) }); |
一开始我并未在意,以为是 Reactjs 的魔术方法,直到阅读如下代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
var Person = function(firstName, lastName) { this.firstName = firstName; this.lastname = lastName; this.gender = "male" }; var Superhero = function(firstName, lastName, powers) { /** * Person(firstName,lastName); * 以上的方式中,this为Window(全局),所以需要手动指定this为Superhero * 在一个子构造函数中,你可以通过调用父构造函数的 call 方法来实现继承 */ Person.call(this, firstName, lastName); this.powers = powers; } //显式的指定继承原型 Superhero.prototype=Object.create(Person.prototype); |
引申出Function
对象的三大原型方法,call()
,apply()
以及bind()
,联想到reactjs中的用法,让我很惊喜,唯一不足的地方是只支持IE9+。
小结
这三个原型方法很少会用到,是偏方,但治疗一些疑难杂症却是有奇效。
我在这里只做一个抛砖引玉的作用,其他用法与例子参考 MDN 的文档是最好的选择,我就不复制粘贴了。
参考链接:MDN:call(),MDN:apply(),MDN:bind()。