Как получить «this» (область видимости) анонимной функции Javascript? - PullRequest
8 голосов
/ 11 октября 2009

Допустим, я получаю анонимную функцию, которая должна воздействовать на ее контекст, но она отличается от того, привязана ли она к "окну" или к неизвестному объекту.

Как получить ссылку на объект, из которого вызывается анонимная функция?

РЕДАКТИРОВАТЬ, некоторый код:

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 и нашел сообщение , в котором говорилось бы использовать "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: полный сценарий, как указано в комментариях.

Я использую 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 функций с разными областями действия, и протестировать их все в цикле, вместо того, чтобы писать вещи вручную.

1 Ответ

6 голосов
/ 11 октября 2009

е-удовлетворяться,

Единственный способ сделать это - использовать метод call или apply для установки правильного «контекста».

Чтобы решить вашу проблему, измените вашу функцию бара так, чтобы она принимала функцию обратного вызова, а также область применения этой функции обратного вызова.

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

bar(ObjectFromOtherLibIAmNotSupposedToknowAbout.foo, ObjectFromOtherLibIAmNotSupposedToknowAbout);

В качестве альтернативы вы можете использовать метод 'bind'.

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

Теперь вы можете оставить нетронутой функцию бара и изменить код вызова так, чтобы он выглядел как

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

EDIT:

Как я отметил в комментарии, вызывающий код отвечает за передачу правильной функции обратного вызова. Ваша функция 'bar' не может определить, какую область использовать, точка.

Возьмем для примера,

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

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

Как вы собираетесь выяснить, какой должна быть сфера? Теперь вам нечего «анализировать», и нет способа изменить функцию «bar», чтобы это работало.

...