41

图解 :一篇彻底带你搞懂JS中的this指向问题

 4 years ago
source link: http://developer.51cto.com/art/201911/605936.htm
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

Javascript是一门基于对象的动态语言,也就是说,所有东西都是对象,一个很典型的例子就是函数也被视为普通的对象。

其中this就是实现面向对象的一个非常重要的特性,但是 this在Javascript非常容易理解错,尤其是对于接触静态语言比较久的同学来说。而且 this又是面试中和实际项目中的重中之重,不得不单独拿出一篇文章来把它理解透透的。

f2UZBba.jpg!web

上面说到this参数是面向对象Javascript编程的一个重要组成部分,代表函数调用相关联的对象,也称为函数上下文。我知道,你可能是个初学 JS 的同学,听不懂,没关系,不用担心,因为下面还有动画来更好的理解。

思维导图

NFVZfeR.jpg!web

1. 什么是 this?

什么是 this呢?上边我们说 this是一个对象,是个啥对象?咱们就来动手敲代码打印一下。我们最常见的 this是在一个函数中, JS 的函数调用有两种方式,一种是我们直接调用,另外一种就是通过 new 的方式来调用,我们通过两种方式来打印一下 this值是否相同?。

6VNFR3v.jpg!web

控制台输出如下:

aYRbiqN.jpg!web

吆喝?咦?虽然都是在函数中,咋打印出来的不一样呢?直接通过函数调用的方式打印出来的 this指向的是全局变量Window;通过new的方式调用的函数当做为构造函数,为了能够创建一个实例对对象,它的 this值指向生成的实例对象。

那我们通过上边的一顿乱操作,知道了 this是一个对象,但是我们不同的操作 this指向的对象是不相同的。写到这里,知道 this是个什么东西就可以了,下面我们再慢慢深入 this原理。

2. 如何判断 this?

既然我们知道 this 是什么东西了,但是怎么判断 this的值呢?也是我们上边没有解决的问题。this的指向有三种情况,只要理解了这三种情况,也不用死记硬背,判断 this如鱼得水,在面试中给面试官讲的滚瓜烂熟。

三种 this指向情况:

(1) 对象调用,this 指向该对象(前边谁调用 this 就指向谁)。

对于第一种情况,通过对象调用的方式,this指向谁?要想一探究竟,必须动手实践一下。小鹿,上代码,好嘞~。

JzQJrya.jpg!web

控制台打印:

7bmIzaM.jpg!web

我们通过亲手测试,我们发现 this的指向就是 obj,所以我们总结归纳为谁调用了函数, this就指向谁,很简单吧,我们继续向下看。

(2) 直接调用的函数,this 指向的是全局 window 对象。

其实这也属于第一种情况,如果我们直接在全局函数调用了函数,其实是全局的对象 Window调用了函数,根据第一条我们得出的结论,谁调用的函数,this就指向谁,想必你已经知道了第二种情况 this指向的就是 Window。

(3) 通过 new 的方式,this 永远被绑定在新创建的对象上,任何方式都改变不了 this 的指向。

第三种方式刚才我们也测试过了,this指向的是指向生成的对象实例,很多好奇的小伙伴就会问小鹿,很好奇 new的内部实现,到底做了什么,其实没什么复杂的,不告诉你估计你也通过上边的两个结论可以得出。

我们从的到的结果进行反推,通过 new的方式 this 指向的是生成的对象实例,那我们猜测肯定内部让这个实例对象调用了函数,所以 this 才指向生成的对象实例。

真实的情况是这样子吗?确实是,我们 new的过程,其实在内部创建了一个空对象,然后将构造函数传入的参数和属性挂在了这个空对象上,然后返回了这个对象。还涉及该空对象到原型链的的挂载,想要具体了解,可以自己探究下,这里不多说了。

扩展:箭头函数的 this 指向谁?

我们都知道 ES6 之后,为了使用函数更加方便,在项目中我们会使用箭头函数,书写方式:

Ar2ae2I.jpg!web

运行程序,控制台输出:

uiuqM3M.jpg!web

我们可以得出结论,this在箭头函数中失效了,因为这是由于箭头函数没有单独的 this值。箭头函数的 this与声明所在的上下文相同。也就是说调用箭头函数的时候,不会隐士的调用 this参数,而是从定义时的函数继承上下文。

有关箭头函数这一点,一定要注意,面试的时候也会经常给你刨坑哦!

3. 如何改变 this 的值?

我们对 this的指向已经把它翻了个底朝天,但是不要傲娇,需要自己找点有关 this 的大厂面试题去做,这样巩固一下加深理解。

this可以指向不同的对象,我们想要改变 this有没有办法呢?有的,改变 this的方法共有三种,我们具体看看这三种方法之间的实现和区别,也是面试重点哦!

(1) call 方法

call方法用来改变 this的指向,具体咱们先看实例:

nqYjimF.jpg!web

控制台输出如下:

Yr6fIjI.jpg!web

(2) apply 方法

我们再来看apply方法,同样举个例子:

rEb6juq.jpg!web

控制台输出如下:

UfAZfmf.jpg!web

我们发现输出的值相同,我们先不比较两者的区别是什么,我们继续往下看bind函数。

(3) bind 方法

我们在用 bind函数举个例子:

IrUNVvy.jpg!web

控制台输出如下:

j2uYFvj.jpg!web

(4) call、apply、bind 三者的区别是什么?

我们对于三者都进行举例运行了,我们开始做总结归纳,我们先找找三者的共同点是什么?

共同点:

  • 都能改变 this 指向,第一个传递的参数都是 this 指向的对象。
  • 三者都采用的后续传参的形式。

三者相同点表面上都能看出来,功能都是一样的,但是对于不同点,就涉及到细节了,不知道你发现了没有?

不同点:

  • call的传参是单个传递的,而 apply后续传递的参数是数组形式,而 bind没有规定,传递值和数组都可以。
  • call和 apply函数的执行是直接执行的,而 bind函数会返回一个函数,然后我们想要调用的时候才会执行。

你可能会有疑惑,小鹿,难道你就是这样表面看出的吗?虽然我们表面可以看得出,那不妨我们自己手写一个 call、apply、和 bind的吧,看了源码的实现,你会觉得这三个函数也没有什么难的。因为涉及到时间问题,我们就不展开讲了,小鹿把代码贴到下边了,感兴趣的可以研究一下。

手写 call 函数:

RraMj2i.jpg!web

手写 apply 函数:

ZjMJZ3f.jpg!web

手写 bind 函数:

RbeEV3F.jpg!web

PS:如果我们使用上边的方法改变箭头函数的 this 指针,会发生什么情况呢?能否对齐进行改变呢?

由于箭头函数没有自己的this指针,通过 call() 或 apply() 方法调用一个函数时,只能传递参数(不能绑定this),他们的第一个参数会被忽略。


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK