首先是,this不能从英语语法的角度来理解,并不指向函数自身,同样的,this也不能简单地认为是指向函数的词法作用域。
this,是函数的执行上下文,使用this是一种传递上下文对象的优雅方式,可以让API的设计更简洁更易于复用。
this,或者说函数的执行上下文,只有在函数被调用的那一刻,this才确定下来。是的,this是什么,取决于函数的调用方式。而函数的调用方式,跟茴字的写法一样,有四种。
1、作为函数直接调用。
2、作为的对象的方法被调用。
4、使用call、apply方法来调用。
3、作用new构造函数来使用。
1、作为函数直接调用
function test(){ console.log(this.a)}
var a = 0
test() // console.log(0)
这种情况下,this是默认指向了该定义函数的作用域。
如果是全局作用域的情况下,this指向window对象 (严格模式下为undefined)。
2、作为对象的方法被调用,隐式地为函数绑定执行上下文对象。
这个说法可能会有些误导,实际上对象不拥有函数,对象只是拥有函数的指针,无论这个函数是在函数内定义还是被当做引用属性添加到对象内的,因而实际情况会因此而变得复杂,为了便于行文,暂时还是称之为对象的方法。
用对象方法这种方式来调用函数的话,this会隐式绑定到对象上去,obj.fun()的this指向a对象,obj对象成为fun函数的执行上下文。
function test(){
console.log(this.a)
}
var obj ={
a:1,
fun:test
}
var a = 0
obj.fun() //console.log(1)
不过,函数这种隐式地绑定一个执行上下文对象有一个问题,就是会丢失掉这个对象。
function test(){
console.log(this.a)
}
var obj = {
a:'inner',
innerFun:test
}
var outterFun = obj.innerFun
var a = 'outter'
outterFun() // console.log('outter')
在这一系列的操作中,先声明了一个函数test,然后在obj对象内添加了一个属性innerFun,innerFun的值是test函数,再之后定义了一个outterFun函数,值是obj.innerFun。
调用outterFun函数的结果是打印出了"outter"。
看上去,innerFun的执行上下文是obj对象,但是前面说过函数的this只有在调用的那一刻才会确定下来,而对象从来不会拥有函数,只是拥有函数指针,于是outterFun实际上指向的是test()函数,自然而然的,this绑定的执行上下文对象就不是obj对象了。
(把test()函数放在在对象内直接定义,也是这样的结果,outterFun是这个函数的指针,指向这个函数的内存地址)
3、使用new来调用函数。
在使用new表达式时,JS先创建一个新的空对象,然后进行prototype链接,这个新对象就作为执行上下文对象被绑定到函数的this上。再之后,如果函数没有返回其他对象,那么new表达式返回的就是这个对象。
function Test (){
var a = 0
console.log(this.a)
}
var a =1
var test = new Test() //console.log(undefined)
在这个过程当中,会打印出一个undefined,这是因为this对执行上下文对象是new操作符创建出来对一个全新的空对象。
4、使用call、bind来调用函数,显式地为函数绑定执行上下文对象。
既然函数会自动绑定this的执行上下文对象,同样的,也可以手动为this指定执行上下文对象。 JS的所有函数都可以调用call方法和apply方法,在call方法和apply方法中传入一个对象,即可实现手动绑定函数的执行上下文对象。
function test(){
console.log(a)
}
var a = 0
var obj = {
a:1
}
test.call(obj) //console.log(1)
call和apply的不同点仅仅是传入参数的区别,call是依次传参,apply是传入一个包含了要传入参数的数组参数。
———
这四种绑定方式的优先级,最低的是默认绑定,然后是隐式绑定,接着是使用call/apply的显式绑定,最后才是new操作符。
这里唯一的困惑就是new操作符的优先级为何会比显示绑定的要高。
这是因为new调用函数的话,如果this被显示绑定的话,new会使用新创建的对象来替换显式绑定的对象。之所以new要在内部替换this,是为了初始化函数的一些参数,这样就可以在使用new调用函数的时候只传入我们的参数。
在ES6中,还有一种情况,与上述的四种情况截然不同,就是箭头函数,表现形式为()=>{},箭头函数的this并不是在调用的时候确定的,而是被固定为声明时所处的执行上下文。