ECNAScript规范中这样描述this:
this 关键字执行为当前执行环境的 ThisBinding。
MDN上这样描述this:
In most cases, the value of this is determined by how a function is called.
在绝大多数情况下,函数的调用方式决定了this的值。
在JavaScript中,this的指向是调用时决定的,而不是创建时决定的。this的不确定性是把双刃剑,一是函数调用时的对象不确定性,是js中函数的使用具有很大灵活性,每个对象都可以借用其他函数来完成功能。二是这也造成了this学习的一些迷惑性。
调用位置
调用位置就是函数在代码中被调用的位置,而不是声明的位置。
通过分析调用栈(到达当前执行位置多调用的所有函数)可以找到调用位置。
var obj = {
foo: function () {}
};
var foo = obj.foo;
// 写法一
obj.foo()
// 写法二
foo()
上面的代码中,obj.foo()和foo()指向同一个函数,但是执行结果可能不一样,差异的原因,在于函数体内使用了this关键字,对obj.foo()来说,foo运行在obj的执行上下文中,this指向obj,对于foo()来说,foo()指向执行上下文,this指向执行上下文。因此,运行结果不一样。
全局上下文
在全局执行上下文中,this指向全局对象
- this等价于window对象
- var === this. === window
在非严格模式中,当this指向undefined时,它会被自动指向全局对象。在实际开发中,现在基本已经全部采用严格模式了,而最新的ES6,也是默认支持严格模式。
console.log(window === this); // true
var a = 1;
this.b = 2;
window.c = 3;
console.log(a + b + c); // 6
在浏览器里面,this等价于window对象,如果声明了全局变量,则全局变量会作为this的属性。
函数上下文
函数内部,this的值取决于函数被调用的方式,在函数执行过程中,this一旦被确定,就不可更改了。
如果调用者函数,被某一个对象所拥有,那么该函数在调用时,内部的this指向该对象。如果函数独立调用,那么该函数内部的this,则指向undefined。
直接调用
this指向全局变量
function foo(){
return this;
}
console.log(foo() === window); // true
call()、apply()
this指向绑定的对象
var person = {
name: "axuebin",
age: 25
};
function say(job){
console.log(this.name+":"+this.age+" "+job);
}
say.call(person,"FE"); // axuebin:25
say.apply(person,["FE"]); // axuebin:25
say()本身没有name、age属性,通过call()和apply()绑定到person对象上,输出的就是属于person的属性,此时this指向person。
call()和apply()从this的绑定角度上来说是一样的,唯一不同的是它们的第二个参数。
call(thisArg, arg1 ,arg2 ...); //直接接收参数传递给函数
apply(thisArg, [arg1 ,arg2 ...]) //接收一个参数数组
bind()
this将永久绑定到bind的第一个参数。bind和call、apply有些相似。
var person = {
name: "axuebin",
age: 25
};
function say(){
console.log(this.name+":"+this.age);
}
var f = say.bind(person);
console.log(f());
//bind()方法创建一个新的函数, 当这个新函数被调用时this键值为其提供的值,
//其参数列表前几项值为创建时指定的参数序列。
//fun.bind(thisArg[, arg1[, arg2[, ...]]])
箭头函数
所有的箭头函数都没有自己的this,都指向外层。
MDN对于箭头函数的描述:
An arrow function does not create its own this, the this value of the enclosing execution context is used.
箭头函数会捕获其所在上下文的this值,作为自己的this值。
function foo() {
setTimeout(()=>{
console.log(this.a);
},100)
}
var obj = {
a: 2
}
foo.call(obj); //2
箭头函数常用语回调函数中,例如定时器中
作为对象的一个方法
this指向调用函数的对象
var person = {
name: "axuebin",
getName: function(){
return this.name;
}
}
console.log(person.getName()); // axuebin
作为一个构造函数
this被绑定到正在构造的新对象
通过构造函数创建一个对象其实执行这样几个步骤:
- 创建新对象
- 将this指向这个对象
- 给对象赋值(属性、方法)
- 返回this
所以this就是指向创建的这个对象上。
function Person(name){
this.name = name;
this.age = 25;
this.say = function(){
console.log(this.name + ":" + this.age);
}
}
var person = new Person("axuebin");
console.log(person.name); // axuebin
person.say(); // axuebin:25
作为一个DOM事件处理函数
this指向触发事件的元素,也就是事件处理程序所绑定的DOM节点
var ele = document.getElementById("id");
ele.addEventListener("click",function(e){
console.log(this);
console.log(this === e.target); // true
})
HTML标签内敛时间处理函数
this指向所在的DOM元素
jQuery的this
在许多情况下JQuery的this都指向DOM元素节点。
总结
如果要判断一个函数的this绑定,就需要找到这个函数的直接调用位置。然后可以顺序按照下面四条规则来判断this的绑定对象:
- 由new调用:绑定到新创建的对象
- 由call或apply、bind调用:绑定到指定的对象
- 由上下文对象调用:绑定到上下文对象
- 默认:在严格模式下绑定到 undefined,否则绑定到全局对象。
注意:箭头函数不使用上面的绑定规则,根据外层作用域来决定this,继承外层函数调用的this绑定。没有外层函数,则是绑定到全局对象(浏览器中是window)