理解JavaScript中的this指向

2,030 阅读3分钟

一、常规函数中this

this是使用常规方法调用函数时(call)传递的第一个参数,它可以在函数调用时修改,在函数没有调用的时候,this的值是无法确定的。

1. 纯粹的函数调用

常见写法(简写):

function test(name) {
    console.log(name)
    console.log(this)
}
test('Jerry')  //调用函数

完整写法:

function test(name) {
    console.log(name)
    console.log(this)
}
test.call(undefined, 'Tom')

如果你传的context 是 null 或者 undefined,那么 window 对象就是默认的 context(严格模式下默认 context 是 undefined)

2. 对象中函数的调用

默认this

const obj = {
    name: 'Jerry',
    greet: function() {
        console.log(this.name)
    }
}
obj.greet()  //第一种调用方法(语法糖)
obj.greet.call(obj) //第二种调用方法

手动指定this

const obj = {
    name: 'Jerry',
    greet: function() {
        console.log(this.name)
    }
}
obj.greet.call({name: 'Spike'})  // Spike

3. 构造函数中this

构造函数里的this稍微有点特殊,每个构造函数在new之后都会返回一个对象,这个对象就是this,也就是context上下文。

function Test() {
    this.name = 'Tom'
}
let p = new Test()
console.log(typeof p)  // object
console.log(p.name)    // Tom

new关键字会创建一个空的对象,将this指向这个空对象,这样的话函数内部的this就会被这个空的对象替代。

当this碰到return时

function fn()  
{  
    this.user = 'Jerry';  
    return {};   // undefined
    return function(){};  // undefined
    return 1;  // Jerry
    return undefined;  // Jerry
}
var a = new fn;  
console.log(a.user); 

如果返回值是一个对象,那么this指向的就是那个返回的对象,如果返回值不是一个对象那么this还是指向函数的实例。

function fn()  
{  
    this.user = 'Jerry';  
    return null;
}
var a = new fn;  
console.log(a);  // fn {user: "Jerry"}

虽然null也是对象,但是在这里this还是指向那个函数的实例,因为null比较特殊。

4. window.setTimeout()和window.setInterval()中函数的调用

window.setTimeout()和window.setInterval()的函数中的this有些特殊,里面的this默认是window对象。

二、箭头函数中的this

MDN官方文档:箭头函数不会创建自己的this,它只会从自己的作用域链的上一层继承this。

1. 箭头函数的特性一:继承上层this

不使用箭头函数

const obj = {
	a: function() { console.log(this) }    
}
obj.a()  // {a: ƒ} obj对象

使用箭头函数

const obj = {
    a: () => {
        console.log(this)
    }
}
obj.a()  // window

2. 箭头函数的特性二:不能用call方法修改里面的this

例子

const obj = {
    a: () => {
        console.log(this)
    }
}
obj.a.call('123')  // window

结合window.setTimeout()

const obj = {
    a: function() {
        console.log(this)
        window.setTimeout(() => { 
            console.log(this) 
        }, 1000)
    }
}
obj.a.call(obj)  //第一个this是obj对象,第二个this还是obj对象

三、多层对象嵌套里函数的this

例子1

const obj = {
    a: function() { console.log(this) },
    b: {
    	c: function() {console.log(this)}
	}
}
obj.a()  // obj对象, 相当于obj.a.call(obj)
obj.b.c() // obj.b对象, 相当于obj.b.c.call(obj.b)

例子2

const obj = {
    a: function() { console.log(this) },
    b: {
    	c: () => {console.log(this)}
	}
}
obj.a()   // obj对象
obj.b.c()  // window!!

window对象就是它的上一层this,此例中的多层嵌套只是对象嵌套,这时候没有作用域链的嵌套,实际上对箭头函数来说,还是只有自己一级的作用域,和上一层的window作用域

例子3

function fn0() {
    return {
        fn1: function () {
             var obj = {
                a: function() { console.log(this) },
                b: {
        	        c: () => console.log(this)
    	        }
    	    }
    	    return obj;
        }
    }
}

fn0().fn1().b.c() // 得到{fn1: f} fn1对象

在ES5中,只有全局作用域和函数作用域,并没有块级作用域,所以这里箭头函数仍然绑定外层this值,而非根作用域