this是js中最复杂的机制之一,程序中经常因为this指向问题导致不能得到预期的结果,这里总结一下代码中常见的this指向问题.
1.声明在全局作用域中的函数
先看下面的例子:
function foo(num){
console.log("foo:" + num);
this.count++;
}
foo.count=0;
var i;
for(i=0;i<10;i++){
if(i>5){
foo(i)
}
}
console.log(foo.count);
上面代码输出结果是0.原因是这里的foo函数的this始终指向执行时的作用域,虽然调用了4次foo函数,但每次调用时,foo函数里的this指向的都是window,所以foo的count属性实际上没有变动过.将上面例子稍微改一下如下:
function foo(num){
console.log("foo:" + num);
this.count++;
console.log(this);//window
}
foo.count=0;
var count=0;
var i;
for(i=0;i<10;i++){
if(i>5){
foo(i)
}
}
console.log(foo.count);
console.log(count); // 4
这样就很好地展示了4次调用foo时,函数里的this均指向的window,而且在全局作用域下定义的同名变量count会被自增4次变成4.
那么如何才能让foo.count变成4呢,需要在调用时使用call函数将foo里的this指向自身,foo.call(foo,i).
2.作为方法在对象中定义的函数
在没有call,apply,bind强行改变this指向时,作为方法在对象中定义的函数里的this一般会指向当前对象.
var obj={
a:2,
foo:function(){
console.log(this.a)
}
}
obj.foo();//2
但是,如果声明一个函数为obj.foo,再调用这个函数,输出的就不再是obj.a了,即在上述例子的基础上加上
var bar=obj.foo;
bar();
第二次输出的就是undefined,因为这里bar虽然是obj.foo的一个引用,但引用的是foo函数本身,bar函数执行时的this指向的还是window,由于没有全局变量a,所以打印出undefined.
3. 匿名函数中的this
js中匿名函数的执行环境是全局性的,所以,在没有call,apply等改变函数执行环境的情况下,匿名函数里的this指向window.
var name = 'window'
var person = {
name :'Alan',
sayName:function () {
return function () {
console.log(this.name)
}
}
}
person.sayName()() // window
4.箭头函数里的this
与普通函数不同,箭头函数的this指向的是定义时所在的作用域.考虑下面的例子:
function Timer(){
this.s1=0;
this.s2=0;
//箭头函数
setTimeout(()=> {
this.s1++;
console.log(this);//Timer
},1000);
//普通函数
setTimeout(function(){
this.s2++;
console.log(this);//window
},1000)
}
var timer=new Timer();
setTimeout(()=>console.log(timer.s1,timer.s2),1100);// 1 0
上述代码中Timer函数里的两个定时器,分别使用了箭头函数和普通函数,前者this绑定定义时所在的作用域,即Timer函数,后者的this指向运行时所在的作用域(即全局对象window).
5.事件绑定中的this
事件绑定的this指向会因为绑定方式的不同,this的指向也不一样.
1) 直接在元素上绑定,this指向window
<button id="aa" onclick="clickme()">点我</button>
function clickme() {
console.log(this); // Window
}
2) js方式绑定事件,this指向当前元素
<button id="aa">点我</button>
document.getElementById('aa').onclick = function () {
console.log(this); // <button id="aa">点我</button>
}
3) react事件绑定中this指向问题
class Button extends React.Component {
constructor(props) {
super(props);
this.state = {
count: 0
};
}
increment() {
this.setState({
count: this.state.count + 1
}) // this为undefined
}
render() {
return (
<div>
<button onClick = {this.increment}>点我</button>
</div>
);
}
}
这里调用this指向的是increment函数,所以是undefined,想要指向组件,可以在constructor函数里将函数的this绑定到此组件上,
this.increment = this.increment.bind(this);
还有一种方法,不用显示绑定this到组件的方法,就是使用箭头函数绑定事件.
<button onClick={()=>this.increment()}>点我</button>
4) vue中的事件绑定
vue中事件绑定的this指向的是vue对象,所以在data()中定义的变量,在函数里可以用this直接访问.但有时候我们需要获得dom元素本身,该如何做呢?
<button @click="clickme()">点我</button>
clickme(){
console.log(this);//vue对象
}
这样要想获得button元素本身,则需要用如下方法:
<button @click="clickme($event)">点我</button>
clickme(e){
console.log(e.target);//<button>点我</button>
}