JavaScript的this词法(二)

this提供了一种优雅的方式来隐式“传递”一个对象引用,因此可以将API设计得更加简洁并且易于复用。随着使用模式越来越复杂,显式传递上下文对象会让代码变得混乱,而使用this则不会这样。(this引用的是函数据以执行的环境对象)
对于this首先要明确的是this绝不是指向函数自身
这里写图片描述

fun()被调用了一次,但是fun.count却为0,并非是想象中的1,在执行fun(2)时,无意中定义了一个全局的count。很明显,this指向的不是函数自身,否则,fun.count此时应该是1.
对于这个问题,我们可以引入fun的标识符替代this来引用函数对象。
这里写图片描述

在调用函数时,我们会将调出处执行环境对象传给this,如此处调用fun(2),处在全局作用域中,因此this会指向window。既然这样,我们可以通过强制this指向fun函数对象来解决这个问题,JS提供了两个函数,apply()和call()函数。
apply()和call()函数的用途都是在特定的作用域中调用函数,换句话说,就是指定this对象的值。
这里写图片描述

fun.call(fun,2);//强制this指向fun,即在调用fun时,将fun传递进去,作为this的值。
补充下apply()和call()的区别:
apply()方法接收两个参数:一个是在其中运行函数的作用域(可以理解为是this的值),另一个是参数数组(可以是arguments或者是数组的实例)
call()方法与apply()方法的作用相同,区别仅在于接收参数的方式不同。第一个参数都是this的值,但是使用call()方法,必须要将传递的参数逐个列举出来。
这里写图片描述

此处,是否使用call()将window对象传递进去,并无影响。
这里写图片描述

结果为NAN,因为全局对象中并无num1和num2的定义,那么为什么是NAN,而不是undefined呢,这就和类型转换扯上关系了,在做加法运算时,会先将其转化为Number类型,undefined类型不是一个数字,因此为NAN
这里写图片描述

结果为20,我们在全局对象中定义了num1和num2
这里写图片描述

结果为20,尽管我们进行了强制绑定,但是可惜绑定的this是window对象。
这里写图片描述

结果依旧为20,是不是觉得有点奇怪了呢?我也很奇怪,这个完全是个意外,让我有点搞不明白JS底层究竟是如何运作的,看起来参数似乎传错了,但是却能得到结果(此处留一个疑问,待明白的时候再补充了)。我的本意是写在下面这样:

这里写图片描述

结果为30,原因是因为我们利用call函数,使得this值指向了add.因此计算的是add.num1+add.num2,结果就是30.
OK,现在我们再换一种写法:
这里写图片描述

结果为20,我们在调用callAdd时,同样使用call去指定this的值,但是却没有效果,这是为什么呢?有些资料上称此为硬绑定(add.call(window, num1, num2))认为后续不能修改硬绑定的this.这种说法正确,但是有时并不是那么好理解。换个角度想,我们在调用callAdd时,使用call强制的使this值指向add,但是,callAdd函数内部并没有使用this变量,它在调用add时,使用call函数将this绑定在了window上。因此我们在执行callAdd.caa(add, num1, num2)时,实际上就是计算window.num1+window.num2,结果显然是20.

对于this我们还要明确一点是this在任何情况下都不会指向函数的词法作用域。作用域“对象”无法通过JavaScript代码访问,它存在于JavaScript引擎内部。

这里写图片描述

这个例子在《You Don’t Know JS》中被认为是试图跨越边界,使用this来隐式引用函数的词法作用域,当然,对于此段代码作者的意图我并不了解,但是对于这段代码,我认为我们只需要去关注this指向的究竟是什么,很明显这儿的this都是指向全局对象window(非严格模式下)。通过this.bar()是不可能使得bar()函数中的作用域为foo()中的作用域的,this不可能会指向词法作用域。
到了这儿,我们不免要思考,this究竟是什么这样的问题。
那么this究竟是什么呢?
首先this是运行时进行绑定的,不是在编写时绑定,它所指代的对象取决于函数被调用时的各种条件。this的绑定和函数声明的位置没有任何关系,只取决于函数的调用方式。

对于文中的疑点,如您知道原因,欢迎留言。
希望本文能对您学习JavaScript的this词法提供一点帮助。

文章导航