JavaScript中的this总是指向一个对象,具体指向哪个对象是在运行时基于函数的执行环境动态绑定的而非函数被声明时的环境决定,或者可以直接记住,this永远指向最后调用它的那个对象。
this指向的四种情况
- 作为对象的方法调用
- 作为普通函数直接调用
- 通过构造器new出来调用
- 通过call、apply、bind方法调用
通过对象调用
当函数作为对象的方法调用时,this指向该对象
var name = "小芳";
var obj = {
name:"小明",
sayName(){
console.log(this.name);
}
}
obj.sayName(); //小明
作为普通函数直接调用
当函数直接执行时相当于是全局对象调用的,此时this的指向是window
var name = "小芳";
var obj = {
name:"小明",
sayName(){
console.log(this);
}
}
obj.sayName(); //小明
let fun = obj.sayName; //此时是吧obj.sayName这个函数赋值给了fun
fun(); //小芳 相当于window.fun();
当然如果在严格模式(use strict)下,这里会报错,因为严格模式下的全局对象是undefined,所以此时会报错Uncaught TypeError: Cannot read property 'name' of undefined
通过构造器创建的对象调用
此时需要分两种情况
1.构造器函数的返回值不是对象
function MyObj(){
this.name = "小明";
}
let obj1 = new MyObj();
console.log(obj1.name); //小明
此时this的指向就是构造器所创建的实例对象
2.构造器函数的返回值是一个对象
function MyObj(){
this.name = "小明";
return{
name:"小芳"
}
}
let obj1 = new MyObj();
console.log(obj1.name); //小芳
此时由于构造器函数返回的是一个对象,此时this的指向是构造器函数所返回的这个对象。
通过call、apply、bind方法调用
通过apply方法调用
apply(thisArg,argsArray)
apply方法可以作用在函数身上,该方法有两个参数第一个参数(thisArg)是一个对象,表示函数内部this所要指向的对象,可以传null,当函数不处于严格模式下,则null或者undefined会被替换为全局对象。
第二个参数是一个类数组对象,用于指定调用函数时所传入的参数,可以通过arguments获取。
let obj1 = {
name:"小明",
sayName(){
console.log(this.name,arguments);
}
};
let obj2 = {
name:"小芳"
}
obj1.sayName(); //小明 [Arguments]{}
obj1.sayName.apply(obj2,[1,2,3,4,5]); //小芳 [Arguments] { '0': 1, '1': 2, '2': 3, '3': 4, '4': 5 }
通过call方法调用
call方法与apply方法作用大致相同,仅仅是传入的第二个参数不同,apply传入的第二个参数需要是个类数组,而call方法除了传入的第一个参数作为this的指向,其他后面传入的参数都会作为传递给fun函数的参数,可以通过arguments访问。
let obj1 = {
name:"小明",
sayName(){
console.log(this.name,arguments);
}
};
let obj3 = {
name:"小风"
}
obj1.sayName(); //小明 [Arguments] {}
obj1.sayName.call(obj3,1,2,3,4,5); //小风 [Arguments] { '0': 1, '1': 2, '2': 3, '3': 4, '4': 5 }
通过bind方法改变this指向
bind()方法创建一个新的函数, 当被调用时,将其this关键字设置为提供的值,在调用新函数时,在任何提供之前提供一个给定的参数序列。
let obj1 = {
name:"小明",
sayName(){
console.log(this.name,arguments);
}
};
let obj4 = {
name:"小红"
}
obj1.sayName(); //小明 [Arguments] {}
obj1.sayName.bind(obj4,1,2,3,4,5)(); //小红 [Arguments] { '0': 1, '1': 2, '2': 3, '3': 4, '4': 5 }
调用bind函数会放回一个新的函数,这个函数的函数体与前一个函数相同,只是函数中的this指向不同。
其他改变this指向的方法
在es5中会有类似于下列这种情况
var name = "windowsName";
var a = {
name : "Cherry",
func1: function () {
console.log(this.name)
},
func2: function () {
setTimeout( function () {
consoloe.log(this.name) //windowsName
this.func1()
},100);
}
};
a.func2() // this.func1 is not a function
在上述代码中,执行setTimeout函数的实际上是window,所以内部函数的this也是window,但是window中没有func1函数,所以会报错this.func1 is not a function。
可通过两种方式解决
保存this
在执行setTimeout函数之前保存func2函数中的this指向
var name = "windowsName";
var a = {
name : "Cherry",
func1: function () {
console.log(this.name)
},
func2: function () {
var _this = this;
setTimeout( function () {
console.log(_this.name); //Cherry
_this.func1()
},100);
}
};
a.func2() // Cherry
使用箭头函数
首先箭头函数中的this指向始终指向函数定义时的环境而非执行时候的环境,或者说箭头函数没有属于自己的执行,其中的this指向的是箭头函数所在的环境。箭头函数中没有 this 绑定,必须通过查找作用域链来决定其值,如果箭头函数被非箭头函数包含,则 this 绑定的是最近一层非箭头函数的 this
如下:setTImeout中的箭头函数的this指向是setTimeout函数所在环境下的this,即func2中的this
var name = "windowsName";
var a = {
name : "Cherry",
func1: function () {
console.log(this.name)
},
func2: function () {
setTimeout(()=>{
console.log(this.name); //cherry
this.func1()
},100);
}
};
a.func2() // Cherry