大数据

简单-JS中的this

什么是this??

this不是函数自身的引用也不是函数词法作用域的引用,this实际上是函数被调用时建立的一个绑定,指向完全由函数被调用时的调用点来决定

  • JavaScript 中的 this 含义要丰富得多,它可以是全局对象、当前对象或者任意对象,这完全取决于函数的调用方式。JavaScript 中函数的调用有以下几种方式:作为对象方法调用,作为函数调用,作为构造函数调用,和使用 apply 或 call 调用
  • this不是编写时绑定,而是运行时绑定。它依赖于函数调用的上下文条件。this绑定和函数声明的位置无关,反而和函数被调用的方式有关

  • 当一个函数被调用时,会建立一个活动记录,也称为执行环境。这个记录包含函数是从何处(call-stack)被调用的,函数是 如何 被调用的,被传递了什么参数等信息。这个记录的属性之一,就是在函数执行期间将被使用的this引用。

分析如下

this是如何不让函数得到他自身的引用的

this是如何不让函数得到他自身的引用的

这种失败来源于对于this (在this.count++中)的含义进行了 过于字面化 的解释,当代码执行foo.count = 0时,它确实在函数对象foo中加入了一个count属性。但是对于函数内部的this.count引用,this其实 根本就不 指向那个函数对象,即便属性名称一样,但根对象也不同,因而产生了混淆
不小心创建了一个全局变量count而且当前的值是NAN
一般开发者试图的解决方案

  • 词法作用域

虽然这种方式确实“解决”了问题,但不幸的是它简单地忽略了真正的问题——缺乏对于this的含义和其工作方式上的理解——反而退回到了一个他更加熟悉的机制的舒适区:词法作用域
词法作用域是一个完善且有用的机制,不是再如何使用this而退回词法作用域的有效解决方案
为了从函数对象内部引用它自己,一般来说通过this是不够的。你用通常需要通过一个指向它的词法标识符(变量)得到函数对象的引用。

cc.png

第一个函数,称为“命名函数”,foo是一个引用,可以用于在它内部引用自己。

但是在第二个例子中,传递给setTimeout(..)的回调函数没有名称标识符(所以被称为“匿名函数”),所以没有恰当的办法引用函数对象自己。

注意: 在函数中有一个老牌儿但是现在被废弃的,而且令人皱眉头的arguments.callee引用 也 指向当前正在执行的函数的函数对象。这个引用通常是匿名函数在自己内部访问函数对象的唯一方法。然而,最佳的办法是完全避免使用匿名函数,至少是对于那些需要自引用的函数,而使用命名函数(表达式)。arguments.callee已经被废弃而且不应该再使用。

  • 对于当前我们的例子来说,另一个 好用的 解决方案是在每一个地方都使用foo标识符作为函数对象的引用,而根本不用this

    这种方法也类似地回避了对this的 真正 理解,而且完全依靠变量foo的词法作用域。

    这种方法也类似地回避了对this的 真正 理解,而且完全依靠变量foo的词法作用域。

  • 另一种解决问题的方法是强迫this指向foo函数对象:

this的作用域

第二常见的对this的含义的误解,是它不知怎的指向了函数的作用域。这是一个刁钻的问题,因为在某一种意义上它有正确的部分,而在另外一种意义上,它是严重的误导。

明确地说,this不会以任何方式指向函数的 词法作用域。作用域好像是一个将所有可用标识符作为属性的对象,这从内部来说是对的。但是JavasScript代码不能访问作用域“对象”。它是 引擎 的内部实现。

考虑下面代码,它(失败的)企图跨越这个边界,用this来隐含地引用函数的词法作用域:

这个代码段里不只有一个错误。虽然它看起来是在故意瞎搞,但你看到的这段代码,是从公共的帮助论坛社区中被交换的真实代码中提取出来的。真是难以想象对this的臆想是多么的误导人。

首先,试图通过this.bar()来引用bar()函数。它几乎可以说是 碰巧 能够工作,我们过一会儿再解释它是 如何 工作的。调用bar()最自然的方式是省略开头的 this.,而仅对标识符进行词法引用。

然而,写下这段代码的开发者试图用this在foo()和bar()的词法作用域间建立一座桥,使得bar()可以访问foo()内部作用域的变量a。这样的桥是不可能的。 你不能使用this引用在词法作用域中查找东西。这是不可能的。

每当你感觉自己正在试图使用this来进行词法作用域的查询时,提醒你自己:这里没有桥。

你不懂JS:this豁然开朗!