JavaScript中的this

124 阅读4分钟

首先是,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并不是在调用的时候确定的,而是被固定为声明时所处的执行上下文。