谈谈js里的this和new
前言
前段时间公司一位前端实习生过来请教js中的this指向问题。这个问题确实是前端开发中经常会遇到的问题。但是这个问题比较杂,回答起来有些费劲。还好,最后能够让他弄清楚这块知识点。这也让我有写这篇博客的想法。
简单的说,传统函数中的this指向的执行该函数时运行环境,ES6箭头函数中的this则指向的定义该函数时的运行环境。
传统函数中的this
我们来看几个🌰:
|
|
第一次执行函数test
时,是在全局的环境下,所以this
指向的是window
,this.a
也就为全局定义的a
了。
第二次执行test
时,我们通过call
方法来改变了test
函数的执行环境,这时的this
指向的就是context
对象了,所以也就打印出了3
。
从中我们可以看出,函数中的this
会随着执行时的上下文环境的改变而改变。
|
|
这里我们定义了一个构造函数Test
,然后通过它实例化了testObj
对象,执行testObj
的c
方法,由于c
方法是在testObj
的环境下运行的,所以这里的this
执行的是testObj
这个对象,也就打印出了内部属性a
。
类似的d
方法一样的也是在testObj
的环境下运行的,结果却打印出了0
,也就是全局的a
变量,这是为什么?
其实这里涉及到了异步的知识,d
方法内部是执行了一个定时器,0.1s后运行一个匿名函数,打印出this.a
。d
函数是在testObj
的环境下执行的,可是这个的匿名函数根据异步函数的运行原理可以知道,它是在全局的环境下运行的。所以这里的this
也就指向的是window
。(关于异步,后面会专门写一篇博客谈谈)
我们如何实现在这个匿名函数中打印出testObj的a属性?代码如下:
方法一是平时最常用,将异步执行的函数中的this
替换成普通变量that
,并提前将需要的运行环境赋值给that
,这样就不存在异步函数中this
指向不明确的问题了。
方法二和方法一类似,只是通过传参的方式,将需要的运行环境传入。
方法三则是通过call
方法改变匿名函数的运行环境。
有人会问直接运行下面代码,会导致什么现象
主要弄清楚了前面说的this
的指向问题,那么这就不存在什么困难了。这就是在全局的环境下执行Test
函数,那么this
指向的就是window
。所以运行后的结果就是在window
对象上绑定了a
,b
,c
三个属性。
有同学会对testObj
的产生过程表示疑惑,var testObj = new Test()
在我的理解用代码表示就是:
箭头函数中的this
随着ES6
的出现,给我们带来了很多特性。其中的箭头函数是其中一个比较重要的特性,极大的简化了我们的代码。并且很好的解决了函数中this
的指向问题。
可以将下面的代码和上面同一部分的代码对比着看:
其中的改动就是把定时器中匿名函数改成了箭头函数,我们发现d方法的执行结果有了变化。这是因为箭头函数中的this执行的始终是定义这个函数时的上下文,不会随着执行的环境改变而改变。这个箭头函数是在testObj的环境下定义的,所以这里的this.a也就是内部属性a了。
有人会对定义时的环境有点不太清楚
其实箭头函数内的this(b处)和 d函数内部的this(a处)是一样的,而a处的this是根据d的运行环境确定的。
所以就有了下面的运行结果: