不要再问我this的指向问题了

this的指向已是一个老生常谈的问题,每逢面试都要去复习复习,近来巩固js的基础,决心完全掌握这个知识点,一劳永逸。说明一下,为了避免影响你们的思考过程,下面的代码都不会去注释答案,想知道答案,只须要去控制台执行一下。面试

四类场景逐一击破

首先,分析this的指向共有四种类型,在分析以前,咱们首先带好两个锦囊:
1.函数被调用时(即运行时)才会肯定该函数内this的指向。由于在函数中this与arguments是两个特殊的变量,在函数被调用时才会取得它们,并且搜索这两个变量时只会在活动对象范围里面去搜。(有关活动对象与变量对象的知识,请移步到js 中的活动对象 与 变量对象 什么区别?
2.要肯定函数中this的指向,必须先找到该函数被调用的位置。浏览器

认准第一种“test()”形式

var a = 1
function test () {
    console.log(this.a)
}
test()

直接不带任何引用形式去调用函数,则this会指向全局对象,由于没有其余影响去改变this,this默认就是指向全局对象(浏览器是window,Node中是global)的。这个结论是在非严格模式的状况下,严格模式下这个this实际上是undefined的。安全

认准第二种“xxx.test()”形式

var a = 1
function test () {
    console.log(this.a)
}
var obj = {
    a: 2,
    test
}
obj.test()

这种形式对比起第一种,很明显test()已是名花有主的了!看清楚,是谁呼唤的test()?没错,就是obj,因此this的指向就不言而喻了。一句话,谁去调用这个函数的,这个函数中的this就绑定到谁身上。app

var a = 1
function test () {
    console.log(this.a)
}
var obj = {
    a: 2,
    test
}
var obj0 = {
    a: 3,
    obj 
}
obj0.obj.test()

即便是这种串串烧的形式,结果也是同样的,test()中的this只对直属上司(直接调用者obj)负责。再来看一个综合点的例子:函数

var a = 1
function test () {
    console.log(this.a)
}
var obj = {
    a: 2,
    test
}
var testCopy = obj.test
testCopy()

嗯,聪明的你必定想到,换了个名字就能骗到我了!?虽然通过了一波更名换姓,但本质上还不是obj.test()嘛!结果必定和上面同样!唔,请F12在控制台试试,居然……其实这里并不须要去思考什么,按照咱们的套路,咱们就认函数调时的样子,有没有看到最后调用的时候跟第一种状况一毛同样?我再介绍一个场景你们必定不会以为陌生:this

var a = 1
function test () {
    console.log(this.a)
}
var obj = {
    a: 2,
    test
}
setTimeout(obj.test)

你能够意淫一下setTimeout的本质,是否是至关于有一个setTimeout函数,接收两个参数:code

function setTimeout (fn, time) {
    // 这里干了一大波不可描述的事情,最后会去调一下你传进来的回调函数
    fn()
}

看到怎样调用你传进来的函数了吗!?再想一想咱们第一种形式的标题认准第一种“test()”形式对象

认准第三种“test.call(xxx) / test.apply(xxx) / test.bind()”形式

看了上面两种形式以后,你可能会想,我很是讨厌上面那些矫情的扭扭捏捏的九曲十八弯的调用方式,让人毫无安全感,我要我本身指定this的指向,我要胜天半子!没问题,个人代码我作主:作用域

var a = 1
function test () {
    console.log(this.a)
}
var obj = {
    a: 2,
    test
}
var testCopy = obj.test
testCopy.call(obj)

能够看到,咱们经过call(apply跟call的区别只是传参,做用是同样的,bind有点区别,bind能让咱们的函数延迟执行,apply与call调用就执行,因此bind这样的形式咱们也称为函数柯里化,这些就不是咱们这里要说的啦)来调用testCopy,而且传入了你想要this指向的上下文,那么this就会乖乖按照你的指示行事啦。看到这里,咱们也能够想象第1、二种形式其实能够转化成call/apply的形式,有一篇比较棒的文章描述了这样的思考过程,你们也能够看看this 的值究竟是什么?一次说清楚get

认准第四种“new test()”形式

终于到了最后一种形式了,这种形式比较好认,由于有标志性的new:

var a = 1
function test (a) {
    this.a = a
}
var b = new test(2)
console.log(b.a)

new这个操做符实际上是new了一个新对象出来,而被new的test咱们称为构造函数,咱们能够在这个构造函数里定义一下将要到来的新对象的一些属性。那么在构造函数里,咱们怎样去描述这个还未出生的新对象呢?没错,就是用this。因此构造函数里的this指的就是将要被new出来的新对象。

One more thing

感谢你们看到这里,但还要说最后一种形式。等等,不是说好的只有四种形式吗!稍安勿躁,正常套路下确实只有上面四种,可是有个东西别忘了,就是你们最喜欢的箭头函数。

var a = 1
var test = () => {
    console.log(this.a)
}
var obj = {
    a: 2,
    test
}
obj.test()

来,往上翻一下咱们的第一个锦囊,“函数被调用时(即运行时)才会肯定该函数内this的指向。”如今函数这两个字要加个词修饰一下,变成普通函数(非箭头函数)才能区别于箭头函数。箭头函数中的this在函数定义的时候就已经肯定,它this指向的是它的外层做用域this的指向。

最后

咱们最后还要说:“到此为止,真的没有了。”但愿看完这篇文章以后,再有人问this指向的问题,你能够嘴角微微上扬,冷笑一声:“不要再问我this的指向问题了。”扬长而去。

相关文章
相关标签/搜索