Получение лучшего понимания функций обратного вызова в JavaScript


163

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

var myCallBackExample = {
    myFirstFunction : function( param1, param2, callback ) {
        // Do something with param1 and param2.
        if ( arguments.length == 3 ) {
            // Execute callback function.
            // What is the "best" way to do this?
        }
    },
    mySecondFunction : function() {
        myFirstFunction( false, true, function() {
            // When this anonymous function is called, execute it.
        });
    }
};

В myFirstFunction, если я возвращаю новый callback (), тогда он работает и выполняет анонимную функцию, но это не кажется мне правильным подходом.


Правильно в каком смысле? Обычно обратные вызовы используются для обработчиков событий - в частности, Ajax-вызовов, которые являются асинхронными, - в основном это вещи, в которых вы не знаете, когда (или если) произойдет повторное восстановление.
— Клет

2
кстати, аргументы похожи на массив, но не на массив, поэтому вы не можете сделать аргумент. длина, но вы можете преобразовать его в массив, используя метод среза ...
— Пол

1
@paul, хотя вы и правы, что argumentsэто не массив, вы все равно можете ссылаться на его длину как arguments.length... попробуйте. Это свойство относится к числу фактически переданных аргументов, а не обязательно к числу параметров в сигнатуре функции.
— hotshot309

Ответы:


132

Вы можете просто сказать

callback();

В качестве альтернативы вы можете использовать callметод, если вы хотите настроить значение thisвнутри обратного вызова.

callback.call( newValueForThis);

Внутри функции thisбудет все, что newValueForThisесть.


91

Вы должны проверить, существует ли обратный вызов и является ли он исполняемой функцией:

if (callback && typeof(callback) === "function") {
    // execute the callback, passing parameters as necessary
    callback();
}

Многие библиотеки (jQuery, dojo и т. Д.) Используют аналогичный шаблон для своих асинхронных функций, а также node.js для всех асинхронных функций (nodejs обычно передается errorи dataдля обратного вызова). Просмотр их исходного кода поможет!


Почему вы приводите callbackк строке, а затем проверяете ее тип? Повысит ли это производительность? Это все равно что проверить тип, проверить, возвращает ли преобразованное логическое значение true, а затем снова проверить его тип и проверить его на соответствие строке. Не могли бы вы объяснить, почему?
— headacheCoder

Мне любопытно, почему вам нужно первое утверждение для обратного вызова ... это проверить нулевое или неопределенное? Не typeof(callback)достиг бы этого для вас? typeof(null) === "Object",typeof("undefined") === "undefined"
— PJH

1
Короткое замыкание И. Если обратного вызова не существует, не беспокойтесь о вычислении его типа. Хотя ты прав. Это не нужно с typeof (), но я сделаю jsperf и посмотрю, стоит ли короткое замыкание.
— arunjitsingh

@headacheCoder - callbackне приводится к строке, ее тип проверяется, чтобы узнать, является ли она функцией, прежде чем она будет вызвана. Код предположительно принимает callbackв качестве аргумента и не уверен, что аргумент имеет вызываемый тип - или, возможно, аргументы имеют различные типы в попытке обеспечить форму полиморфизма, где код может по-разному реагировать на разные typeofаргументы.
— LeeGee

34

Есть 3 основных варианта выполнения функции:

var callback = function(x, y) {
    // "this" may be different depending how you call the function
    alert(this);
};
  1. обратный вызов (аргумент_1, аргумент_2);
  2. callback.call (some_object, аргумент_1, аргумент_2);
  3. callback.apply (some_object, [аргумент_1, аргумент_2]);

Выбор метода зависит от того:

  1. У вас есть аргументы, хранящиеся в массиве или в виде отдельных переменных.
  2. Вы хотите вызвать эту функцию в контексте некоторого объекта. В этом случае использование ключевого слова this в этом обратном вызове будет ссылаться на объект, переданный в качестве аргумента в call () или apply (). Если вы не хотите передавать контекст объекта, используйте null или undefined. В последнем случае глобальный объект будет использоваться для «этого».

Документы для Function.call , Function.apply


6

Обратные вызовы - это сигналы, а «new» - создание экземпляров объектов.

В этом случае было бы еще более уместно выполнить просто «callback ();» чем "return new callback ()", потому что вы все равно ничего не делаете с возвращаемым значением.

(И тест arguments.length == 3 действительно неуклюж, правда, лучше проверить, что параметр обратного вызова существует и является функцией.)


6

правильная реализация будет:

if( callback ) callback();

это делает параметр обратного вызова необязательным.


Что если аргумент обратного вызова не является функцией?
— Яки Кляйн

2

Ты можешь использовать:

if (callback && typeof(callback) === "function") {
    callback();
}

Приведенный ниже пример является немного более полным:

function mySandwich(param1, param2, callback) {
  alert('Started eating my sandwich.\n\nIt has: ' + param1 + ', ' + param2);
  var sandwich = {
      toppings: [param1, param2]
    },
    madeCorrectly = (typeof(param1) === "string" && typeof(param2) === "string") ? true : false;
  if (callback && typeof(callback) === "function") {
    callback.apply(sandwich, [madeCorrectly]);
  }
}

mySandwich('ham', 'cheese', function(correct) {
  if (correct) {
    alert("Finished eating my " + this.toppings[0] + " and " + this.toppings[1] + " sandwich.");
  } else {
    alert("Gross!  Why would I eat a " + this.toppings[0] + " and " + this.toppings[1] + " sandwich?");
  }
});


1

Вот базовый пример, который объясняет callback()функцию в JavaScript:

var x = 0;

function testCallBack(param1, param2, callback) {
  alert('param1= ' + param1 + ', param2= ' + param2 + ' X=' + x);
  if (callback && typeof(callback) === "function") {
    x += 1;
    alert("Calla Back x= " + x);
    x += 1;
    callback();
  }
}

testCallBack('ham', 'cheese', function() {
  alert("Function X= " + x);
});

JSFiddle


Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.