
地 址:上海市嘉定66号
网址:xfcd.net
邮 箱:10874764@qq.com
JS中的到底this是一个老生常谈的问题了,因为它并不是指向知道一个确定的(de)值,在(zai)不同情况下有不同的(de)完篇指(zhi)向,所(suo)以也经常使人困惑。到底本篇文章会谈谈我自(zi)己对this的指向知道理解。
this到底是完篇啥

其实this就是一个指针,它指示的(de)到底就是(shi)当前的一个执行环境,可以用来对当(dang)前执行环境进行一些操作。指向知(zhi)道因为它指示的完篇是执行环境,所以在(zai)定(ding)义这个变量时,到底其实是指向知道(dao)不知道它真正的值的,只有运行时才能确定他的完篇值。同样一段代码,到底用不同的指向知道方式执行(xing),他的完篇this指向可能是不一样的。我们来看看如下代码:

这个方(fang)法很简单,只是给this添加了(le)一个name属性,我们把这个方法复制到Chrome调试工具看下结果:

上图中我们直接调用了func(),发(fa)现this指向的是window,name属性添加到了window上。下面我们换一种调用方式(shi),我们换成(cheng)new func()来调用:
我们看到输出了两个func { name: "小小飞"},一个是我(wo)们new返回的对象,另一个是(shi)方法里面的console。这两个值是一样的,说明这时候方法(fa)里面this就指(zhi)向了(le)new返回的对象,而不(bu)是(shi)前面例子的window了。这(zhe)是因为当你使用new去调用一个方法(fa)时,这个方法其实就作为构造函数使用了,这时候的this指向的是new出来的对象。
下面我们分别讲解下几种情况
使用new调用时,this指向(xiang)new出(chu)来的对象
this的这(zhe)种特(te)性还(hai)有一些妙用。一个函数可以直接调用(yong),也(ye)可以用new调用,那假如我只想使用者通过new调(diao)用有没有办法呢?下图截取自Vue源码:
Vue巧妙利(li)用了this的特性,通过检查this是(shi)不是Vue的一个实例来检测使用者是通过new调用的还是(shi)直接调用的。
没(mei)有明确调用者时,this指向window
这个其实在最开始的例子就讲过了,那里没有明确调用(yong)者,this指向的是window。我们这里讲另外一个例子,函数里面的函数,this指向谁?
我们执行一下看看:
直接执行:
使用new执行(xing):
我们发现无论是直接执行,还是使用new执行,this的值都指向的window。直(zhi)接执行时很好理解,因(yin)为没有明确调用者,那this自然就是window。需要注意的(de)是(shi)使(shi)用new时,只有(you)被new的func才是构造函数(shu),他的(de)this指向new出来的对象,他里面的函数的this还是指向window。
有明确调用者时,this指向调用者
看这个例子:
上(shang)述例子很好理解,因为调用者是(shi)obj,所以func里面的this就指(zhi)向obj,this.myName就是(shi)obj.myName。其实这一条和上一条可以合在一起,没有明确调用(yong)者时其实隐含的调用者就是window,所以经常有人说this总是指向调用者。
下面我们将(jiang)这个例子稍微改一下:
这里的输出应该是“大飞哥”,因为虽然anotherFunc的函数(shu)体跟obj.func一样(yang),但(dan)是他(ta)的执行环境不一样,他其实没有明确的调用者,或者说调(diao)用者是window。这里的this.myName其实是(shi)window.myName,也就是“大飞哥”。
我们将这个例子再改一下:
这次我们只是将第一个var改成了let,但是我(wo)们的(de)输出却变成了undefined。这是因为let,const定义变量,即使在最外层也不会(hui)变成window的(de)属性,只有(you)var定(ding)义的变量才会成为window的属性。
箭头(tou)函数并不会绑定this
这句话的意思是箭头函数(shu)本身并不具有this,箭头函数在被申明确定this,这时候他会直接将(jiang)当前作用域的this作为自己的this。还是之前的例子我们将函数改为(wei)箭头函数:
再来看一个例子:
两个输出都是“小小飞”,obj.func().getName()输出(chu)“小(xiao)小飞”很好(hao)理解(jie),这里箭头函数是在(zai)obj.func()的返回值里(li)申明的,这时他的this其实就是func()的(de)this,因为他是被obj调用的,所以this指向obj。
那为什么anotherFunc()输出也是“小小飞”呢?这是因为anotherFunc()输出的(de)this,其实在anotherFunc赋值时就确定了:
再来看一个构造函数里面的箭头函数,前面(mian)我们说了构造函数里面的函数(shu),直接调用(yong)时,他的this指向window,但是如果这个函数时箭头函数呢:
这里输出的是“小小飞”,原理还是一样(yang)的,箭头(tou)函数在申明(ming)时this确定为当前作用(yong)域的(de)this,在这里就是func的作用域,跟func的this一样指向new出来的实例。如(ru)果不用new,而是直接调用,这里的(de)this就指向window。
DOM事件回调里面(mian),this指(zhi)向绑定事(shi)件的对象
严格模式下this是undefined
注意这里说的严格模式下this是undefined是指在函数体内部,如果本身就在全局作用域,this还是指(zhi)向(xiang)window。
this能改吗
this是能改(gai)的,call和apply都可(ke)以修改this,ES6里(li)面还新增了一个bind函数。
使用call和apply修改this
注意上面输出的名(ming)字是"小小飞",也就是(shi)obj2.myName。正常直接调用obj.func()输出的名字应该是obj.myName,也就是"大飞哥"。但(dan)是如果你使用call来调(diao)用,call的第一个参数就(jiu)是手动指定的this。我们将它指定为obj2,那(na)在函数里(li)面的this.myName其实就是obj2.myName了(le)。
apply方法跟call方法作用差不多,只是后面的函数参数形(xing)式不同,使用apply调用应该这样写,函数参数(shu)应该放到一个数组或者(zhe)类数组里面:
之所(suo)以有call和apply两个方(fang)法实现了差不多的功能,是为(wei)了让大家使用方便,如果你拿到的参数是一个一个的,那就使用call吧,但是(shi)有时候(hou)拿到的参(can)数是arguments,这(zhe)是函数的一个内置变量,是一个类数组结(jie)构,表示当前函数的所有参数,那就(jiu)可以直接用apply,而不用将它展开了。
使用bind修改this
bind是ES5引入的一个方法,也(ye)可以修改this,但是调用它并不会立即执行方(fang)法本身,而是(shi)会返回一个修改了this的新方法:
bind和call,apply最大的区别就是call,apply会立即执行方法,而bind并(bing)不会立即执行,而是会返回一个新方法供后面使用。
bind函数也可(ke)以接收多个参数,第(di)二个及以后的参数(shu)会作为新(xin)函数的参数传递进去,比如前(qian)面的bind也可以这样写:
自己写一个call
知道了call的(de)作用,我们自己来写一个call:
自己写一个apply
apply方法跟call方法很像,区别只是在取调用参数上(shang):
自己写一个bind
自己写一个bind需要用到前面的apply,注意他的(de)返回值(zhi)是一个方法
总结
文章的最后,感谢你花(hua)费(fei)宝贵的(de)时(shi)间阅读本文,如果本文给了(le)你(ni)一点点(dian)帮助或者启发,请不要(yao)吝啬你的赞和GitHub小星星,你的支持是作者持续创作的动力(li)。
【责任编辑:庞桂玉