关于JS中this指向问题,简单地说就是:哪个对象调用了函数,函数中的this就指向这个对象。按以下几种情况进行讨论:
1. 普通函数调用
var num = 10
function fun1(){
console.log(this) /* Window */
console.log(num) /* 10 */
}
fun1()
/*
相当于: window.fun1()
*/
这种情况下,我们直接使用fun1()调用函数,其实就是window.fun1(),所以this就是指向的是调用函数的对象——window对象。
2. 对象函数调用
var num = 10
var obj={
a:12,
fn:function(){
cons.log(this) /* {a: 12, fn: f} */
console.log(this.a) /* 12 */
console.log(this.num) /* undefined */
}
}
obj.fn()
可以看到调用fn函数的是obj对象,所以this指向的是obj对象,obj对象中没有num,所以打印出来是undefined。
3. 构造函数调用
function Person(name,age){
this.name = name
this.age = age
}
var p = new Person('tanjia','18')
/*
上一步相当于:
p.name = 'tanjia'
p.age = '18'
*/
console.log(p) /* Person {name: "tanjia", age: "18"} */
构造函数就是用new 操作符去调用一个函数,这个构造函数内部的this指向新的实例,如:
var p = new Person('tanjia','18')
过程中this指向p。
关于new操作符创建对象时发生的事情:
第一步: 创建一个Object对象实例
第二步: 将构造函数的执行对象赋给新生成的这个实例
第三步: 执行构造函数中的代码
第四步: 返回新生成的对象实例
4. apply、call、bind调用
apply,call,bind都是来改变this的指向,它们的第一个参数是this指向的那个对象,但是apply接受的第二个参数是数组,call和bind接受的参数是一系列单独的变量。
apply与call立即执行
bind返回一个函数,如果要执行需要再调用
例如:
var obj = {
a:1,
b:2,
add(name) {
console.log(this.a + this.b)
console.log(name)
}
}
var a = {
a:2,
b:2,
}
var b = {
a:4,
b:2,
}
var c = {
a:5,
b:6,
}
/* this指向a对象,2+2=4 */
obj.add.call(a,"call") /* 4 call */
/* this指向b对象,4+2=6 */
obj.add.apply(b,["apply"]) /* 6 apply */
/* this指向c对象,5+6=4 */
obj.add.bind(c,"bind")() /* 11 bind */
从上面的代码中可以看出,我们利用call、apply、bind来改变this指针的指向,传入的第一个参数即是this的指向。
5. 箭头函数调用
箭头函数没有this, 它的this是继承而来,默认指向在定义它时所处的对象(宿主对象), 而不是执行时的对象。
var obj = {
a:10,
b:function(){
console.log(this) /* {a: 10, b: ƒ} */
setTimeout(function(){
console.log(this) /* Window */
console.log(this.a) /* undefined */
})
}
}
obj.b()
通过上面代码可以看到,函数obj.b()中的this指向的是obj这个对象,而在setTimeout中函数是普通函数,它的this指向的是window对象。
现在,我们将setTimeout中的函数改为箭头函数,上述中我们知道箭头函数没有this,this是继承而来。
var obj = {
a:10,
b:function(){
console.log(this) /* {a: 10, b: ƒ} */
setTimeout(()=>{
console.log(this) /* {a: 10, b: ƒ} */
console.log(this.a) /* 10 */
})
}
}
obj.b()
可以看到,箭头函数中的this指向了obj对象(继承了外部的this),可以访问对象中的元素。