在前端中this的作用机制可以说和原型,作用域等一样非常重要。本文在此就《你不知道的Javascript》(上)中所提到的this相关的内容作一定的记录和总结
1.1、为什么要用this
在解释为什么要使用this之前,先来一段代码
从上图代码中可以看到,在不同的上下文对象(me和you)中我们重复调用了函数identify()和speak(),不用针对每个对象编写不同版本的函数。原因是我们在上述代码中使用了.call(this)来调用函数。
如果是不使用this,那就需要给identify()和speak()显示地传入一个上下文对象。如下面代码所示:
对比上面两端代码可以看出,使用this的情况下,代码显得更加整洁规范,即看起来更加优雅。
小结:为什么要使用this:
this提供了一种更优雅的方式来隐式“传递”一个对象引用,可以将API设计得更加简介并且易于复用。随着代码中的使用模式越来越复杂,显示传递上下文对象会让代码变得越来越混乱,使用this可以避免这种情况的发生。
1.2 关于this的误解
1.2.1 指向自身
不明白this具体机制的时候,通常会错误地认为this指向函数自身,这个错误的理解主要是从英语语法和翻译本身的角度产生的。
在说明this为什么不是指向函数自身之前,我们先来了解下指向函数本身这个概念。
什么样的情况下需要指向函数自身?最常见的情况就是递归(从函数内部调用这个函数,通俗地说就是我调用我自己),或者是写一个在第一次被调用后自己解除绑定的事件处理器。
现在来分析下,为什么this不是指向函数自身。下面编写一段代码,代码的目的是记录函数foo调用的次数。
从上面两张图中可以看出,直接从foo函数内部console.log出来的语句有4条输出,说明foo()确实是被调用了4次,但是foo.count却还是0。显然从字面意思来理解this是错误的。
分析原因:执行foo.count=0时,的确向函数对象foo添加了一个属性count。但是函数内部代码this.count的this并不是指向那个函数对象,所以虽然属性名相同,根对象却不相同,因此产生错误理解。
为了能让输出的foo.count表示foo函数调用的次数,这种情况就需要从函数对象内部引用它自身了。与前面所说的this指向函数对象不同的是,要完成从函数对象内部引用它自身,仅仅只使用this是不公的。一般来说还需要通过一个指向函数对象的词法标识符(变量)来引用它。
例如:方案一:使用foo标识符来替换foo函数中的this,这样就能完成直接指向函数自身的目的,代码如下图:
方案一中打印出的结果foo.count的最终值为4,是我们想要的结果,但是该方案中直接用foo代替了this,这实际上是回避了this的问题。
因此,为了更深入的探讨thisd饿作用机制,我们还是采用this,只是这里需要对this作一定的处理,让this强制指向foo函数对象。即方案二,代码如下图:
使用call(...)可以确保this指向函数对象foo本身,打印出来的结果和方案一的结果相同。
在此,通过方案一和方案二两种方式完成了打印foo函数执行次数的功能,在实际的开发中通常使用方案二较好。具体的关于this的详细原理会在后续解释。
1.2.2 它的作用域
第二种常见的误解是:this的作用域是指向函数的作用域。
需要明确的是this在任何情况下都不指向函数的词法作用域。在javascript的内部,作用域确实和对象类似,可见的标识符都是它的属性。但是作用域“对象”无法通过javascript代码访问获取到,它存在与javascript引擎内部。
1.3 this到底是什么
排除了上面所说的两种错误理解之后,来看看this到底是一种什么机制。
首先,this是在运行时进行绑定的,并不是在编写时绑定,它的上下文环境取决于函数调用时的各种条件。this的绑定和函数声明的位置没有任何关系,只取决于函数的调用方式。
其次,当一个函数被调用时,会从创建一个活动记录(有时候也成为执行上下文)。这个记录会包含函数在哪里被调用(调用栈)、函数的调用方式、传入的参数等信息。this就是这个记录的一个属性,会在函数执行的过程中用到。
1.4 小结
对于javascript开发者来说,this的绑定是一件非常重要的事情,虽然时常会让人感到困惑。
学习this的第一步是明白this既不指向函数自身也不指向函数的词法作用域,抛开以前错误的假设和理解。
this实际上是在函数被调用时发生的绑定,它指向什么完全取决于函数在哪里被调用。
网友评论