嵌套函数中的函数上下文(“this”)

时间:2013-02-03 21:42:15

标签: javascript

当您在Javascript中调用顶级函数时,函数内的 this 关键字将引用默认对象(如果在浏览器中,则为窗口)。我的理解是,这是一个调用函数作为方法的特殊情况,因为默认情况下它是在窗口上调用的(如John Resig的书中所述,JavaScript忍者的秘密,第49页)。实际上,以下代码中的两个调用都是相同的。

function func() {
  return this;
}

// invoke as a top-level function
console.log(func() === window); // true

// invoke as a method of window
console.log(window.func() === window); // true

到目前为止一直很好......现在这是我不理解的部分:

当函数嵌套在另一个函数中并在未指定要调用的对象的情况下调用时,函数内的 this 关键字也指向window。但是不能在窗口上调用内部函数(参见下面的代码)。

function outerFunc() {
  function innerFunc() {
    return this;
  }

  // invoke without window.* - OK
  console.log(innerFunc() === window); // true

  // invoke on window
  //window.innerFunc(); - error (window has no such method)
  console.log(window.innerFunc) // undefined
}

outerFunc();

完全可以理解嵌套函数在窗口上不可用,因为它毕竟是嵌套的...但是后来我不明白为什么this关键字引用窗口,就好像在窗口上调用了函数一样。我在这里缺少什么?

编辑

以下是对以下优秀答案的总结以及我的一些后续研究。

  • 说“正常”调用函数与调用 window 的方法相同是不正确的。只有在全局定义函数时,这才是正确的。

  • 函数上下文( this 关键字的值)不依赖于函数的定义位置和方式,而是取决于函数的调用方式。

  • 假设代码未在严格模式下运行,则“正常”调用函数会将函数上下文设置为window(在浏览器中运行时,或其他环境中的相应全局对象)。

  • 上述规则的一个例外是使用 bind 来创建一个函数。在这种情况下,即使“正常”调用该函数,它也可能具有 window 以外的上下文。也就是说,在这种情况下,上下文由您创建函数的方式决定,而不是如何调用它。虽然严格来说这是不准确的,因为 bind 会创建一个新函数,使用 apply 在内部调用给定函数。该新函数的上下文仍将由其调用的方式决定,但它通过使用 apply 来屏蔽其内部调用的函数的上下文。

通过调用“normal”,我引用了以下简单的调用方式:

myFunction();

为了完成图片,这里简要介绍了其他调用方式和相应的上下文:

  • 作为对象(方法)的属性 - 上下文是对象

  • 使用apply或call - 明确指定上下文

  • 使用 new 运算符(作为构造函数) - 上下文是新创建的对象

为了类似问题的人的利益,请随时根据需要更新上述内容。谢谢!

5 个答案:

答案 0 :(得分:1)

您可以使用functionName()调用范围内的任何函数。由于您尚未在对象上调用它,因此将在默认对象(window)的上下文中调用它。 (IIRC,如果您处于严格模式,将在undefined的情况下调用它。)

上下文的默认对象与定义函数的位置或函数的显示范围无关。它只是默认对象。

如果一个函数是一个对象的属性,你可以将其称为reference.to.object.function(),它将在object的上下文中调用,而不是默认对象。

更改上下文的其他因素是new关键字和applycallbind方法。

答案 1 :(得分:1)

在JavaScript中,当在没有显式上下文的情况下调用函数时,上下文是全局对象。对于Web浏览器,全局对象为window

此外,JavaScript具有功能范围,因此函数内的任何变量或函数都不能在该函数之外的范围内访问。这就是您无法访问window.innerFunc

的原因

答案 2 :(得分:0)

函数是否嵌套在另一个函数中与调用函数时this的值无关。唯一重要的事情是:

  • 如果通过遍历对象的属性“找到”该函数,则this的值将是对该对象的引用:

    someObject.prop( whatever );
    

    声明函数的方式无关紧要。

  • 如果使用call()apply()来调用函数,则this的值将从第一个参数中取出,用于您使用的任何函数。

  • 如果您使用bind()为该函数创建了绑定包装器,则this的值将在调用bind()时按照请求进行。

  • 如果您使用new作为构造函数调用函数,则this将引用新创建的对象实例。

  • 否则,this或者是对全局上下文的引用,或者是undefined(在“严格”模式下或在符合ES5的运行时中)。

    < / LI>

定义函数的代码中的“位置”确实很重要,因为范围包括它包含的任何符号,并且无论如何获得对函数的引用,函数都可以使用这些符号。

答案 3 :(得分:0)

它不依赖于声明函数的位置,而是取决于它的调用方式:

var obj = {
   f: function() {
       return this;
   }
}

var f = obj.f;

console.log(obj.f()) // obj
console.log(f()) // window/default obj

或换句话说。语法obj.f()使用this=obj执行函数,而f()使用this=window执行函数。在JavaScript中,调用者指定this的值。

答案 4 :(得分:0)

在全局范围中定义func时,它实际上被指定为window对象的属性。也就是说,window对象包含所有全局范围的变量。 (*)因此,funcwindow.func代表相同的事物。 innerFunc在函数范围内定义,在该范围之外不可用。因此,window.innerFunc是(仍然)undefined

但是,this上下文取决于您调用该函数的方式。当您调用obj.method()之类的方法时,this上下文设置为obj。另一方面,您也可以单独调用该方法:

var f = obj.func;
f(); // in this call: this === window

在这种情况下,您不是在对象上调用函数,因此this上下文被设置为默认值。然而,默认值是全局范围,如上所述,这由window表示。

您始终可以使用以this上下文作为第一个参数的Function.prototype.call()Function.prototype.apply()覆盖this上下文。例如:

var f = obj.func;
f.call(obj); // in this call: this == obj

(*)请注意,这仅适用于在浏览器中运行的JavaScript。在其他环境中,这可能会有所不同。例如,在Node.js中,GLOBAL变量保存全局范围。