之前也有一篇文章讲过JavaScript的this指向问题,不过感觉那篇讲得比较繁琐,这篇文章就简单再总结一下各种情况下this的指向,大体来说,this是指向代码调用者/执行者,所以在一个对象里面直接this,它指向的就是window:
var age = 24
var obj = {
age:18,
obj2:{
age:this.age
}
}
console.log(obj.obj2.age) //24
现在总结一下四种函数调用情况下this的指向。
-
普通的函数调用
如果函数是直接被调用的,那么this就指向全局对象,在浏览器环境是Window,而严格模式打印undefined。在Node环境下因为每个文件都是一个单独的作用域,所以并不会绑定到node的全局对象中,所以打印undefined。
var a = 4 function foo(){ var a = 5 console.log(this.a) } foo() //打印4
-
对象中调用函数
在对象中调用函数,那么this就指向这个对象。多层嵌套的对象,this指向离被调用函数最近的对象。
var a = 4 function func(){ console.log(this.a) } let obj = { a:2, foo:func } obj.foo() //打印2
-
构造函数调用
通过new关键字来调用函数的话,this则指向新创建的那个实例。
var a = 1 function foo(a){ this.a = a this.func = function(){ console.log(this.a) } } var f = new foo(2) f.func() //打印2
-
通过apply/call/bind绑定
这三个函数会改变函数的this指向,区别在于call可以直接写多个参数,apply以数组的形式传入参数,而bind则是返回一个新的函数。
var a = 4 var obj = { a:2, b:3 } function foo(b,c){ console.log(this.a) console.log(b+c) } foo(5,6) //4,11 foo.call(obj,5,6) //2,11 foo.apply(obj,[5,6]) //2,11 var foo2 = foo.bind(obj,5,6) foo2() //2,11
还有一些特殊情况,比如异步操作时的回调函数中的this会绑定什么呢?
var a = 4 setTimeout(function(){ this.a = 4 console.log(this.a) }) //打印4
运行一下,发现会打印4(严格模式下undefined),可以看出来异步回调中的this指向全局对象,如果看过上一篇事件循环的文章就不难看出来,setTimeout属于宏任务,它会在主线程中执行栈所有任务都执行完了才会被压入执行栈中执行,因此他被调用的地方是在全局作用域中,符合第一种调用情况,因此this就指向全局对象。
ES6提出了箭头函数,与前面所说的不同,箭头函数的会捕获其定义位置上下文的this,作为自己的this值。
var a = 4 function foo() { this.a = 6 setTimeout(function () { var a = 5 console.log(this.a) }) } var f = new foo() //打印4 // ============================= var a = 4 function foo() { this.a = 6 setTimeout(() => { console.log(this.a) }) } var f = new foo() //打印6
总而言之,普通函数内的this,指向函数被调用时所处的对象;而箭头函数内的this,指向定义该函数时所在的作用域的对象。setTimeout的回调函数运行在于所在函数完全分离的执行环境上,若普通函数定义该回调函数,则会导致其this指向window。若用箭头函数定义setTimeout的回调函数,由于定义时所处的上下文环境是foo对象,所以this指向的就是f这个实例。
然而因为这个特性,所以目前来说不能将箭头函数用作对象方法的定义:
var obj = { age:18, getAge:()=>{ return this.age } } console.log(obj.getAge()) //打印undefined
所以想要获得对象内的属性,还是用普通函数,或者将箭头函数包裹在一个普通函数里:
var obj = { age:18, getAge:function (){ return this.age } } console.log(obj.getAge()) //18 // ============================= var obj = { age:18, getAge:function (){ var fn = () => { //这里用普通函数的话this就会指向window return this.age } return fn() } } console.log(obj.getAge()) //18