一道经典JS题(关于this)
2015-12-29 20:28:00 浏览:1256 作者:管理员
项目中碰到的问题,以前也碰到过,没有重视,现记录如下。
<input type='button' value='click me' id='btn' /><script> var num = 0; var obj = { num: 1, fn: function() { console.log(this.num); }, init: function() { setTimeout(this.fn, 0); document.getElementById('btn').addEventListener('click', this.fn, false); } }; obj.init();</script>
输出啥?点击 button 后输出啥?
解决类似的问题其实很简单,只需要牢记下面一句话:
除了 DOM 的事件回调或者提供了执行上下文(call、apply、bind)的情况,函数正常被调用(不带new)时,里面的 this 指向的是全局作用域。
先看第一处,其实可以改写成这样:
setTimeout(function() { console.log(this.num);}, 0);
0ms后 函数正常被调用,里面的 this 指向全局作用域 window,所以输出即为 window.num,即为代码最开始定义的 num=0。
再看第二处,可以改写成这样:
document.getElementById('btn').addEventListener('click', function() { console.log(this.num);}, false);
click 后触发一个回调函数,属于 DOM事件回调,DOM 事件回调中的 this 指向该 DOM 元素。所以输出 this.num 即为 document.getElementById('btn').num,为 undefined。
如何能让 click 事件后输出 1?因为这种需求在生产中很常见,绑定各种事件,回调已经定义的对象方法。很明显,归根结底需要改变函数中 this 指向。
方法一:
再加一层匿名函数,使得 fn 被 obj 调用:
var that = this;document.getElementById('btn').addEventListener('click', function() { that.fn();}, false);
方法二:
用 bind 改变 this 指向:
// 兼容IEFunction.prototype.bind = Function.prototype.bind || function(context) { var that = this; return function() { return that.apply(context, arguments); }}document.getElementById('btn').addEventListener('click', this.fn.bind(this), false);