【问题标题】:Referring to "this" in a parent closure in javascript在 javascript 的父闭包中引用“this”
【发布时间】:2010-10-26 00:57:21
【问题描述】:

我想在 Javascript 中执行此操作:

function Z( f )
{
  f();
}

function A()
{
  this.b = function()
  {
    Z( function () { this.c() } );
  }

  this.c = function()
  {
    alert('hello world!');
  }
}

var foo = new A();
foo.b();

可以这样实现:

function Z( f )
{
  f();
}

function A()
{
  var self = this;
  this.b = function()
  {
    Z( function () { self.c() } );
  }

  this.c = function()
  {
    alert('hello world!');
  }
}

var foo = new A();
foo.b();

有没有更好的办法?

【问题讨论】:

    标签: javascript closures this


    【解决方案1】:

    保持对父级的引用(就像您一样)是一种好方法,但是对于您的具体示例,不需要匿名包装器,您可以直接传递函数,如下所示:

    var self = this;
    this.b = function()
    {
      Z(self.c);
    }
    

    You can test it out here,如果没有这个包装器,实际上就不需要self 变量,你可以直接使用this,像这样:

    this.b = function()
    {
      Z(this.c);
    }
    

    You can test that version here.


    由于下面的 cmets 似乎有些混乱,上面的代码维护了this问题,如果你想在回调中也维护this/context,使用.call()like this:

    this.b = function()
    {
      Z.call(this, this.c);
    }
    

    对于Z:

    function Z( f )
    {
      f.call(this);
    }
    

    You can test it here.

    【讨论】:

    • 对,在这个简单的例子中,包装器没有理由。想出一个必要的例子应该不难。 (更改参数等)如果你不换行,我假设闭包是按预期传递的?
    • 没有匿名包装器,c 被错误的this 引用调用。
    • @Jonathan - 是的,正确,在这种情况下,您需要传递一个变量,就像您拥有 self 一样,可以让您访问闭包中的内容。
    • @Lee - 不是,演示显示了这一点,with 包装器 this 是不正确的引用。
    • @Nick - 函数c 是A 类的成员。它应该能够在内部使用this 来访问A 的其他成员。在没有包装器的情况下通过Z 调用时,情况并非如此。你可以看到这个演示here。
    【解决方案2】:

    你也可以使用

    this.b = function()
    {
        Z( (function () { this.c() }).apply(this) );
    }
    

    【讨论】:

      【解决方案3】:

      有一种通常称为“委托”的模式可以解决这个问题。

      在 javascript 中,不太花哨的实现可能如下所示:

      /** class Delegate **/
      var Delegate = function(thisRef, funcRef, argsArray) {
          this.thisRef=thisRef;
          this.funcRef=funcRef;
          this.argsArray=argsArray;
      }
      Delegate.prototype.invoke = function() {
          this.funcRef.apply(this.thisRef, this.argsArray);
      }
      /** static function Delegate.create - convenience function **/
      Delegate.create = function(thisRef, funcRef, argsArray) {
          var d = new Delegate(thisRef, funcRef, argsArray);
          return function() {  d.invoke(); }
      }
      

      在你的例子中,你会这样使用它:

      this.b = function() {
        Z( Delegate.create(this, this.c) );
      }
      

      您还可以编写期望接收委托的函数:

      function Z( d ) {
          d.invoke();
      }
      

      然后,在A 中,您对b 的实现变为:

      this.b = function() {
          var d = new Delegate(this, this.c);
      
          Z( d );
          SomeOtherFunc( d );
      }
      

      Delegate 只是提供了一种简单、一致的方式来封装this 引用(您称之为self),该对象实例可以像任何其他对象实例一样处理。它更具可读性,它使您不必使用像self 这样的多余变量来污染您的函数范围。更高级的委托实现可以有自己的方法和其他相关状态。也可以构建委托以帮助最小化一些与范围相关的内存管理问题(尽管我在此处显示的代码绝对不是这样的示例)。

      【讨论】:

      • 我认为这行不通:Z( function() { Delegate.create( this, c ) } );。没有包装器,就不需要self,也不需要使用Delegate,因为写Z( this.c ); 会更短。
      • @Jonathan - 你添加了一个额外的包装器,这不在我的示例中。只是Z( Delegate.create(this,c) );我稍微扩展了 Delegate 示例,使其更灵活,更清晰。
      • @Jonathan - Z( this.c ) 可以工作,只要你不关心 this 在 c 中运行时引用的对象。您显示的代码没有使用c 中的this,因此不会出现任何问题。如果您查看(对@Nick 示例的轻微修改)[jsfiddle.net/UQpnX/],您将看到潜在问题,该问题由匿名包装器在您的原始示例中解决 - 或者使用更正式的构造,如 Delegate。任何一个都可以。
      • 如果您必须在回调中调用.invoke(),为什么不在两个位置都使用.call() 或.apply()?这是一种非常简单的方法。
      • @Nick - 你不必在你的回调中调用invoke()。这是我展示的两个示例中的一个。 first 示例是迄今为止这种方法更常见的用法:静态Delegate.create 返回一个简单函数。您可以将其传递到可以传递任何其他功能的任何地方。第二个示例仅显示了另一种用法,它仅在您扩展 Delegate 以包含其他特定于域的功能时才真正有用——在某些情况下,这可能是一个非常理想的功能。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2010-09-25
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多