this实际上是在函数被调用时发生的绑定,this的绑定和函数声明的位置没有任何关系,它指向什么完全取决于函数在哪里被调用。(除了es6箭头函数中的this)
1. 默认绑定
function foo(){
console.log(this.a);
}
var a=2;
foo();//2
函数调用时应用了this的默认绑定,因此this指向全局对象。
如果使用严格模式,则不能将全局对象用于默认绑定,因此this会绑定到undefined:
function foo(){
"use strict";
console.log(this.a);
}
var a=2;
foo();//TypeError:this is undefined
2.隐式绑定
function foo(){
console.log(this.a);
}
var obj={
a:2,
foo:foo
};
obj.foo();//2
当函数有上下文对象时,隐式绑定规则会把函数调用中的this绑定到这个上下文对象。
3.显式绑定
可以使用call(…)和apply(…)、bind(…)、forEach(…)方法。可以直接指定this的绑定对象
function foo(){
console.log(this.a);
}
var obj={
a:2;
};
foo.call(obj);//2
在调用foo时强制把它的this绑定到obj上。使用硬绑定后就无法使用隐式绑定和显示绑定来修改this,所以可以用softbind(…)软绑定来修改this。
如果你把null或者undefined作为this的绑定对象传入call、apply或者bind,这些值在调用时会被忽略,实际应用的是默认绑定规则。
4.new绑定
function foo(a){
this.a=a;
}
var bar=new foo(2);
console.log(bar.a);//2
使用new来调用foo(…)时,我们会构造一个新对象并把它绑定到foo(…)调用中的this上。
以上就是this的四种绑定规则。这四种规则的优先级是:new绑定>显示绑定>隐式绑定>默认绑定。
5.箭头函数中this
ES6中的箭头函数并不会使用四条标准的绑定规则,而是根据当前的词法作用域来决定this,具体来说,箭头函数会继承外层函数调用的this绑定。这其实和ES6之前代码中的self=this机制是一样的。
function foo(){
//返回一个箭头函数
return (a)=>{
//this继承foo()
console.log(this.a);
};
}
var obj1={a:2};
var obj2={a:3};
var bar=foo.call(obj1);
bar.call(obj2);//2,不是3
foo()内部创建的箭头函数会捕获调用时foo()的this。由于foo()的this绑定到obj1,bar的this也会绑定到obj1,箭头函数的绑定无法被修改。
箭头函数常用于回调函数中,例如事件处理器或者定时器:
function foo(){
setTimeout(()=>{
//这里的this在词法上继承自foo()
console.log(this.a);
}.100);
}
var obj={a:2};
foo.call(obj);//2
箭头函数体内的this对象,就是定义时所在的对象,而不是使用时所在的对象。(这里的‘定义时所在的对象’指的是单独的作用域,对象不构成单独的作用域)
const cat = {
lives: 9,
jumps: () => {
this.lives--;
}
}
上面代码中,cat.jumps()方法是一个箭头函数,这是错误的。调用cat.jumps()时,如果是普通函数,该方法内部的this指向cat;如果写成上面那样的箭头函数,使得this指向全局对象,因此不会得到预期结果。这是因为对象不构成单独的作用域,导致jumps箭头函数定义时的作用域就是全局作用域。
var x = 11;
var obj = {
x: 22,
methods: {
x: 33,
say: function () { console.log(this.x) },
say2: () => { console.log(this.x) }
}
}
obj.methods.say();//33
obj.methods.say2();//11