【问题标题】:How to get the "this" (scope) of a Javascript anonymous function?如何获取 Javascript 匿名函数的“this”(范围)?
【发布时间】:2009-10-11 14:11:53
【问题描述】:

假设我得到一个匿名函数,需要对其上下文进行操作,但它是绑定到“窗口”还是绑定到未知对象是不同的。

如何获得对调用匿名函数的对象的引用?

编辑,一些代码:

var ObjectFromOtherLibIAmNotSupposedToknowAbout = {
    foo : function() {
        // do something on "this"
    }
}

var function bar(callback) {
     // here I want to get a reference to 
     // ObjectFromOtherLibIAmNotSupposedToknowAbout
     // if ObjectFromOtherLibIAmNotSupposedToknowAbout.foo is passed 
     // as callback
}

bar(ObjectFromOtherLibIAmNotSupposedToknowAbout.foo);

您可能会问,您到底为什么要这样做。好吧,我首先想解压缩作为数组传递的参数。就像 Python "*" 运算符所做的那样:

>>> args = [1,2,3]
>>> def foo(a,b,c) :
        print a,b,c
>>> foo(*args)
1 2 3

我在 SO 中挖掘并找到了一个 post 告诉使用“apply()”:

function bar(callback, args){
    this[callback].apply(this, args);
}

有趣的是,如果在对象中,它将使用当前的“this”,如果不是,则使用“window”。

但是我觉得有问题:

如果 "bar()" 本身在一个对象中,那么 "this" 将引用 "bar()" 容器,因此它不会工作。

顺便说一句,我不想将范围作为参数传递

我当然可以将参数和函数连接为字符串,然后使用 eval,但我只想在找不到更干净的东西时才使用它。

当然,如果这是不可能的(毕竟,它可能是),那么我会做:

function foo(func, args) 
{
    eval("func("+args.join(", ")+");");
}

编辑 2:完整场景,如 cmets 中所述。

我正在使用 qunit 在 Javascript 中执行单元测试。这很酷,但我错过了一种检查是否引发异常的方法。

最基本的测试就是这样完成的:

/**
 * Asserts true.
 * @example ok( $("a").size() > 5, "There must be at least 5 anchors" );
 */
function ok(a, msg) {
    _config.Test.push( [ !!a, msg ] );
}

我们的想法是制作类似的东西:

jqUnit.prototype.error = function(func, args, msg) 
{
    try 
    {
        eval("func("+args.join(", ")+");");
        config.Test.push( [ false, msg + " expected : this call should have raised an Exception" ] );
    } catch(ex)
    {
        _config.Test.push( [ true, msg ] );
    }
};

如果我能摆脱 eval,那就太好了。为什么我不想将范围用作参数?因为您可能希望循环引用具有不同范围的 20 个函数的容器,并在循环中对它们进行全部测试,而不是手动编写这些东西。

【问题讨论】:

  • 您能提供一些代码示例吗?我很难理解你真正想要什么。
  • 是的,我不确定您在寻找什么。请提供一些代码示例。
  • 请不要使用eval!请理解,这不是您作为 'bar' 函数的作者的问题。谁想要调用你的“bar”函数,必须传递一个“工作”回调函数。
  • 另外,除了给出这个抽象的例子,你能告诉我们实际情况吗?
  • 添加完整场景:-)

标签: javascript this anonymous-function


【解决方案1】:

满足,

唯一的方法是使用 call 或 apply 方法来设置正确的“上下文”。

要解决您的问题,请修改您的 bar 函数以接受回调函数以及应用于该回调函数的范围。

function bar(callback, scope)
{
    callback.apply(scope);
}

bar(ObjectFromOtherLibIAmNotSupposedToknowAbout.foo, ObjectFromOtherLibIAmNotSupposedToknowAbout);

或者,您可以使用“绑定”方法。

Function.prototype.bind = function(context) {
  var fun = this;
  return function(){
    return fun.apply(context, arguments);
  };
};

现在,您可以保持 bar 函数不变,并将调用代码修改为如下所示,

bar(ObjectFromOtherLibIAmNotSupposedToknowAbout.foo.bind(ObjectFromOtherLibIAmNotSupposedToknowAbout));

编辑:

正如我在评论中指出的,调用代码有责任传递正确的回调函数。您的 'bar' 函数无法确定要使用的范围,句号。

以此为例,

var LibObject = { foo: function() { //use this inside method } };

var fooFunction = LibOjbect.foo;
bar(fooFunction);

您将如何确定范围应该是什么?你现在没有什么可以“解析”的了,你也没有办法修改你的“bar”函数来使它工作。

【讨论】:

  • 你好,谢谢,但它是为了破解单元测试框架的一个小扩展,我手头没有测试功能的范围(也不应该有)。
  • 你是否负责编写那个 bar 方法调用?在 JS 中,函数不附加到对象上,除非您使用 call/apply 明确指定它,否则 JS 引擎将决定使用什么上下文。
  • 我是,但我不能要求以某种方式(qunit 语法)、某种签名来编写 1000 个测试,并且为了方便我自己编写的一些小技巧而例外。它会破坏 API 的一致性,这是不值得的。没什么大不了的,这个问题不是生死攸关的问题:-)
  • 您始终可以将“范围”参数设为可选。如果提供了参数,则使用该参数,否则不使用。换个角度来看,调用代码实际上有责任传递正确的回调函数。他们应该使用我上面概述的“绑定”方法。或者,您可以通过采用可选的“范围”参数来帮助您的调用代码。我希望我说得通。
  • 请将范围负担留给调用代码,他们总是可以使用'bind'方法或类似的方法。另外,请不要在您的 JS 代码中使用 EVAL。
猜你喜欢
  • 2011-02-13
  • 1970-01-01
  • 2010-12-19
  • 1970-01-01
  • 2012-05-10
  • 1970-01-01
  • 2011-07-13
  • 2011-10-27
  • 1970-01-01
相关资源
最近更新 更多