Там много чего происходит…

Что, если бы наша функция $(document).ready выглядела так:

Вау, это легко и приятно!

Теперь, если вы говорите: «Эй! какой-то код отсутствует!» — вы правы. Мы переместили код, который был в анонимных функциях, и поместили его в именованную функцию.

Эта функция выглядит так:

function ResetPanel() {
    $.ajax("/admin/import/fuel/deleteStageData", {
        success: function (data) {
            $('#importresults').DataTable().ajax.reload();
            ResetPanelsForNoData();
        },
        error: function () {
            alert('error purging data, please refresh the page');
        }
    });
}

С функцией все еще бардак, но, по крайней мере, теперь она не мешает нам.

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

В javascript вы можете заменить использование анонимной функции именованной функцией, указав имя функции (без кавычек и скобок в конце!)

Давайте рассмотрим несколько примеров, прежде чем мы перейдем к PHP…

//let's assume this is the function we want to call:
function doSomething(data)
{
   // something gets done here.
}

Мы хотим использовать это как анонимную функцию:

Так что же работает?

//works
$('#Button1).click(doSomething);

//does not work:
$('#Button1).click(doSomething());

//does not work:
$('#Button1).click(doSomething(data));

Короче говоря, в JavaScript нам просто нужно имя функции, как если бы это была переменная. Мы не вызываем функцию, поэтому нам не нужен ( ) в конце. Почему мы не вызываем функцию???

Потому что в данном случае мы регистрируем «обратный звонок».

В приведенном выше коде мы «регистрируем» функцию, которая будет вызываться когда нажимается кнопка (что на данный момент не так). Позже, когда кнопка действительно будет нажата, код вызовет нашу функцию, которую мы зарегистрировали.

Надеюсь, это имеет смысл.

Применение именованных анонимных функций в PHP.

В сообществе PHP Laravel является популярным фреймворком, а в сообществе Laravel Адам Ватан выпустил книгу Refactoring to Collections. Эта книга в настоящее время в моде, и это причина, по которой я столкнулся с ней в первую очередь.

В книге Адама он рассказывает о рефакторинге с использованием коллекций Laravel.

Если вы еще не читали книгу, вот краткий пример…

Например, предположим, что переменная $music — это Коллекция всех ваших песен.

Самое классное в коллекциях то, что они возвращают коллекции, поэтому вы можете связать их в цепочку, и есть множество вспомогательных функций, позволяющих вам что-то делать. В этом примере показано, как мы можем взять нашу коллекцию $music, извлечь только поле заголовка и затем отсортировать по названию:

$music()->
   pluck('title')->
   sortBy('title');

Довольно мило, да?

Теперь некоторые из методов коллекции принимают строковые значения, как показано выше с помощью pluck() и sortBy().

Очень многие вспомогательные функции используют анонимные функции для выполнения задач:

$music()->
   each(function($song) {
     return ucase($song['title']);
   });

Я не сторонник анонимных функций. Я чувствую, что они портят хороший и читаемый код. Приведенный выше пример прост, но я хочу, чтобы вы представили какой-нибудь код из реальной жизни, скажем, есть 3-4 метода, связанных вместе, и представьте, что 2 из них используют анонимные функции, и представьте, что эти 2 функции больше, чем строка или два.

Это начало бы выглядеть примерно так:

На первый взгляд, можете ли вы сказать, что делает этот код выше ???

И я нет.

Я бы предпочел, чтобы функция each() вызывала другую функцию.

Нравится:

//note this is for illustration purposes only
//the following code will not work
$music()->
   filter(jazzonly())-> 
   pluck('title')->
   sortBy('title')->
   each(casefix());

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

Это не работает.

Что нам нужно сделать, так это сообщить php имя функции, которую он должен вызвать позже.

Когда мы используем () в вызове функции, PHP думает, что мы говорим ему вызвать эту функцию прямо сейчас.

В идеале мы хотим передать только имя функции, как мы сделали в приведенном выше примере JavaScript, но это не работает в PHP.

Таким образом, следующее не работает:

//does not work in PHP:
filter(jazzonly())->...
//Also does not work in PHP:
filter(jazzonly)->...

Итак, как это можно сделать в PHP???

Способов немного…

1) Назначьте анонимную функцию переменной и включите эту переменную. Этот способ больше всего подходит для javascript, но в PHP он также наименее полезен, поэтому у меня возникает соблазн даже не освещать и не объяснять его.

Если бы вы действительно хотели сделать это таким образом, вы бы сделали:

$variable = function($in){
  return $in >1;
}

Но это не лучший способ в PHP, так что давайте перейдем к #2.

2) Вызов «обычных» функций как анонимных.

Оказывается, в PHP есть несколько способов вызова обычной функции вместо анонимной.

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

Давайте посмотрим, как это выглядит:

class learnbyExample
{
   function GreaterThanOne($input)
   {
      return $input > 1;
   }
 
   function UsesCollections()
   {
     $col = collect([-1,0,1,2,3,4]);
     $col->filter([$this, 'GreaterThanOne']);
   }
}

Теперь мы куда-то попали!

Эта функция «GreaterThanOne» — это просто обычный запуск функции мельницы. Мы можем указать его как функцию обратного вызова, используя массив из двух частей:

1) объект с функцией
2) имя функции

В данном случае функция была из того же объекта, поэтому в качестве объекта я использовал $this.

Примечание: объект не в кавычках, а имя функции!

Больше сценариев

На ум приходят еще два сценария:

1) Мы хотим вызвать метод из другого класса. 2) Мы хотим вызвать статический метод из другого класса.

Что ж, хорошие новости, и то, и другое легко. Синтаксис у всех почти одинаковый.

class Utility
{
   function GreaterThanOne($input)
   {
      return $input > 1;
   }
}

class LearnbyExample
{ 
   function UsesCollections()
   {  
     $utilityClass = new Utility(); 
   
     $col = collect([-1,0,1,2,3,4]);
     $col->filter([$utilityClass, 'GreaterThanOne']);
   }
}

В приведенном выше примере мы вызываем метод из другого класса.

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

File1:
namespace path\to\our\code;

class Utility
{
  static function GreaterThanOne($input)
   {
      return $input > 1;
   }
}
File2:
namespace path\to\our\controllers;

use Illuminate\Support\Collection;

class LearnbyExample
{
   function UsesCollections()
   {
      //note: that $utilityClass = new Utility is not needed here
      //because we're calling a static function...
   
      $col = collect([-1,0,1,2,3,4]);
      $col->filter(['path\to\our\code\Utility', 'GreaterThanOne']);
   }
}

Видите, как эти два файла работают вместе? В файле2 строка:

$col->filter(['path\to\our\code\Utility', 'GreaterThanOne']);

вызывает статическую функцию «GreaterThanOne» из класса «Utility» в пространстве имен «путь\к\нашему\коду»

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

Весь код в файле1 и файле2 в приведенном выше примере одинаков, за исключением одной строки:

$col->filter('path\to\our\code\Utility::GreaterThanOne');

Это означает то же самое, что и в предыдущем примере, за исключением того, что мы вызываем его, используя одну строку, а не разделяя ее на две части.

недостатки этого подхода

Эрик Л. Барнс, мой соведущий в Laravel-News-Podcast, высказал хорошую мысль:

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

Резюме:

Перемещение того, что было бы в анонимной функции, в обычную функцию, дает вам гибкость при написании кода.

Анонимные функции не так уж плохи, когда у них всего одна строка кода, но многострочные (особенно вложенные) анонимные функции могут быстро сделать даже самый простой код нечитаемым.

На заре программного обеспечения не было никаких функций, только переходы, и люди ненавидели это. Функции родились и было много радости! Сегодняшние анонимные функции немного напоминают мне те «ранние времена», когда весь этот код запихивается куда-то, когда вы просто хотите, чтобы он говорил print_invoice().

Я надеюсь, что приведенные выше примеры дадут вам инструменты, необходимые для того, чтобы сделать ваш код более читабельным!

  • Джек

PS, без помощи Адама Ватана я бы не справился — спасибо, Адам! Адам прислал по этой ссылке, которая может оказаться полезной: http://php.net/manual/en/language.types.callable.php

  • Джек