文章目录
this到底是咋回事儿?
1. 概念
先来看看ECMAScript 标准规范对this 的定义:
「The this keyword evaluates to the value of the ThisBinding of the current execution context.」
「this 这个关键字代表的值为当前执行上下文的ThisBinding。」
然后再来看看MDN 对this 的定义:
「In most cases, the value of this is determined by how a function is called.」
「在大多数的情况下,this 其值取决于函数的调用方式。」
this是在函数被调用时发生的绑定,它指向什么取决于函数的调用方式。
2. 全局对象和调用普通函数
在全局环境中,this 指向全局对象,在浏览器中,它就是 window 对象。下面的示例中,无论是否是在严格模式下,this 都是指向全局对象。
var a = 100;
console.log(this.a) //100
console.log(this.a === a) //true
console.log(this === window) //true
如果普通函数是在全局环境中被调用,在非严格模式下,普通函数中 this 也指向全局对象;如果是在严格模式下,this 将会是 undefined。ES5 为了使 JavaScript 运行在更有限制性的环境而添加了严格模式,严格模式为了消除安全隐患,禁止了 this 关键字指向全局对象。
var a = 100;
function foo(){
console.log(this) //window
console.log(this.a) //100
}
看下面这张图
3. 作为对象的方法调用
我们一般把对象里基本类型的值叫属性(字符串String、数值Number、布尔值Boolean),如果值是函数,我们称之为方法。
当函数作为对象的方法被调用时,函数中的this指向这个调用的对象。
var a = 100;
var obj = {
a: 200,
foo: function(){
console.log(this)
console.log(this.a)
}
}
obj.foo()
// {a: 200, foo: ƒ}
// 200
var fn = obj.foo;
fn()
// Window
// 100
在上面代码中,运行obj.foo()时,foo方法的执行对象是obj对象,所以 this指向obj。而当把obj.foo赋值给fn变量时,相对于创建了全局函数fn,所以执行fn(),this指向Window对象。
再看一个案例:
var a = 100
var obj = {
a: 200,
b: {
a: 300,
foo: function () {
console.log(this); // Object {a: 300, foo: function}
console.log(this.a); // 300
}
}
}
obj.b.foo();
执行obj.b.foo()时,foo的调用者变成了b对象,所以this也就指向了b对象。这个案例是在对象中嵌套对象,那如果我们在函数中嵌套函数呢?
var obj = {
b: function () {
console.log(this === obj); // true
console.log(this); // {b: f}
// 执行foo函数
foo();
function foo() {
console.log(this === obj); // false
console.log(this); // Window 全局对象
}
}
}
obj.b();
分析下,执行obj.b()时,b函数的执行对象是obj,所以this指向obj对象,这个没问题。问题是,在函数内执行foo函数,这时foo函数内的this不会指向obj,foo内的this不会继承外面的函数。其实还是那句话,"谁调用了这个函数"
在非严格模式下,this指向全局对象Window,严格模式下指向undefined。
4. 作为构造函数调用
函数除了作为普通函数被调用外,可以使用new关键字,作为构造函数调用。这时候,在构造函数内,this指向实例对象。
function Person(){
this.age = 20;
// Person {age: 20}
console.log(this)
}
var p = new Person()
// 20
console.log(p.age)
如果我们在构造函数返回了引用类型的对象,this会指向这个返回的对象:
function Person(){
this.age = 20;
// Person {age: 20}
console.log(this)
return{
age: 30
}
}
var p = new Person()
// 30
console.log(p.age)
但如果返回了一个基本类型的值,则this仍然指向实例对象。
function Person(){
this.age = 20;
// Person {age: 20}
console.log(this)
return 30
}
var p = new Person()
// 20
console.log(p.age)
5. 通过call和apply方法调用
我们知道通过call和apply方法可以改变this的指向。call和apply方法的第一个参数就是函数运行时this的指向。如果没有传参数(参数为空)或者传入null、undefined,name默认this指向全局对象(非严格模式下)或者为undefined(严格模式下)
call和apply的主要区别在于参数,call第二参数之后是参数列表,可以有多个参数,apply的第二个参数是一个数组。对于this的改变的作用是一样,下面以call方法为例:
var a = 1;
var obj = {
a: 2
}
function foo() {
console.log(this);
console.log(this.a);
}
foo.call(obj)
// Object {a: 2}
// 2
foo.call()
// Window 全局对象
// 1
foo.call(null)
// Window 全局对象
// 1
foo.call(undefined)
// Window 全局对象
// 1
使用 call 和 apply 时,如果给 this 传的不是对象,JavaScript 会使用相关构造函数将其转化为对象,比如传 number 类型,会进行new Number()
操作,如传 string 类型,会进行new String()
操作,如传 boolean 类型,会进行new Boolean()操作。
function foo() {
console.log(this)
}
foo.call('Davie') // [object String]
foo.apply(100) // [object Number]
foo.call(true) // [object Boolean]
6. 通过bind方法调用
bind方法也可以改变函数的调用对象,和call及apply类似,但是call和apply是立即执行。而bind方法只是修改了函数的this指向,并不马上执行。
var a = 100
var obj1 = {
a: 200
};
var obj2 = {
a: 300
};
function foo() {
console.log(this);
console.log(this.a);
};
var f1 = foo.bind(obj1);
var f2 = foo.bind(obj2);
foo();
// Window 全局对象
// 100
f1();
// Object {a: 200}
// 200
f2();
// Object {a: 300}
// 300
7. 箭头函数中的this
箭头函数没有自己的this
绑定。箭头函数中使用的this
,直接是包含它的那个函数或函数表达式中的this
。
修改前面函数嵌套的案例,把里面的函数写成箭头函数:
var obj = {
b: function () {
console.log(this === obj); // true
console.log(this); // {b: f}
var foo = () => {
console.log(this === obj); // true
console.log(this); //{b: f}
}
foo();
}
}
obj.b();
这时箭头函数中的this就会继承完成函数的this,所以也指向了obj对象。
一句话:箭头函数的this继承自外层函数的this。如果有外层函数,那么外层函数的this就是箭头函数的this,如果没有就是全局对象Window。
var obj = {
b: () => {
console.log(this === obj); // false
console.log(this); // Window 全局对象
var foo = () => {
console.log(this === obj); // false
console.log(this); // Window 全局对象
}
foo();
}
}
obj.b()
上例中,存在两个箭头函数,里面foo的this取决于最外层箭头函数b的this,由于obj是个对象而非函数,所以this指向为Window全局对象。
我们无法通过 bind、call 和 apply 来改变 this 的指向,即传入的第一个参数被忽略。
var a = 100
var obj = {
a: 200
}
var foo = () => {
console.log(this.a)
console.log(this)
}
foo.call(obj) // 不报错,只是obj被忽略了
// 100
// Window 全局对象
foo.apply(obj) // 不报错,只是obj被忽略了
// 100
// Window 全局对象
8. 总结
归根结底还是那句话,函数的this指向该函数的调用对象。
最后用一张图总结下this的指向:
End
如果这篇文章有所帮助,请点点赞,点点关注,谢谢大家!