Могу ли я назвать функцию JavaScript и немедленно ее выполнить?

90

У меня их довольно много:

function addEventsAndStuff() {
  // bla bla
}
addEventsAndStuff();

function sendStuffToServer() {
  // send stuff
  // get HTML in response
  // replace DOM
  // add events:
  addEventsAndStuff();
}

Повторное добавление событий необходимо, так как DOM изменилась, поэтому ранее прикрепленные события исчезли. Поскольку они тоже должны быть прикреплены изначально (да), они могут быть СУХИМИ.

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

Оба следующих ответа дают синтаксическую ошибку:

function addEventsAndStuff() {
  alert('oele');
}();

(function addEventsAndStuff() {
  alert('oele');
})();

Есть берущие?

Руди
источник
возможный дубликат самовыполняющейся функции Javascript
Кристиан Санчес,
2
Нет, @CristianSanchez.
Máxima Alekz
Имхо, ваш второй блок кода трудно читать. В первом блоке кода каждому читателю ясно, что вы вызываете функцию после инициализации. Читатели склонны игнорировать закрывающие и перечитывать скобки в конце.
kaiser

Ответы:

123

Нет ничего плохого в примере, который вы разместили в своем вопросе. Другой способ сделать это может показаться странным, но:

var addEventsAndStuff;
(addEventsAndStuff = function(){
    // add events, and ... stuff
})();

Есть два способа определить функцию в JavaScript. Объявление функции:

function foo(){ ... }

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

var foo = function(){};
(function(){})();
var foo = {bar : function(){}};

...так далее

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

(function foo(){
   foo(); // recursion for some reason
}());

но это не так:

(function foo(){
    ...
}());
foo(); // foo does not exist

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

Марк Кан
источник
1
«Функциональные выражения могут быть названы» Остерегайтесь именованных функциональных выражений. Они должны работать так, как вы описываете, но они не работают в IE до IE9 - вы получаете две функции, и имя функции просачивается. Подробности: kangax.github.com/nfe (Это наконец-то исправлено в IE9.)
TJ Crowder,
@TJ - спасибо за ссылку. Я не осознавал этого, поскольку я никогда не использую IE, кроме как для тестирования конечных продуктов :) Я предполагаю, что IE9 не эмулирует эту ошибку при установке его в режим IE7 / 8? Я думаю, что он все еще использует движок IE9 JS ... Мех
Марк Кан
Если, как и я, вы столкнетесь с проблемами jshint с этим методом, вы можете использовать call()метод отсюда: stackoverflow.com/a/12359154/1231001
cfx
Этот подход может показаться полезным в определенных обстоятельствах, но, помимо того, что его немного сложнее читать, он, похоже, имеет практически такое же количество текста.
Владимир
17

Для этого есть хорошее сокращение (не нужно объявлять какие-либо переменные, запрещающие назначение функции):

var func = (function f(a) { console.log(a); return f; })('Blammo')
Джеймс Горри
источник
Что rвернет функция? function rИли var r? Просто любопытно. Хорошее решение. Хотя не обязательно быть одним местом =)
Руди
Я переименовал его, чтобы было понятнее, но это return rбыло бы function rтак, как это было. Я писал Scala так постоянно, пытаясь получить что-то онлайн.
Джеймс Горри
@JamesGorrie, блестящее сокращение, однако я дважды пробовал в консоли, кажется, что func, func (), func () () все декларации функции возврата f (), но можем ли мы вывести Blammo с помощью func (), как ожидалось, пожалуйста :)?
mike652638
@ mike652638 Я запустил это в своей консоли, и консоль регистрирует blammo?
Джеймс Горри
5

В этой настройке нет ничего плохого (или есть?), Но можно ли немного сгладить?

Вместо этого посмотрите на использование делегирования событий. Вот где вы на самом деле наблюдаете за событием в контейнере, который не исчезает, а затем используете event.target(или event.srcElementв IE), чтобы выяснить, где на самом деле произошло событие, и правильно его обработать.

Таким образом, вы прикрепляете обработчик (-ы) только один раз, и они продолжают работать, даже когда вы меняете контент.

Вот пример делегирования событий без использования вспомогательных библиотек:

(function() {
  var handlers = {};

  if (document.body.addEventListener) {
    document.body.addEventListener('click', handleBodyClick, false);
  }
  else if (document.body.attachEvent) {
    document.body.attachEvent('onclick', handleBodyClick);
  }
  else {
    document.body.onclick = handleBodyClick;
  }

  handlers.button1 = function() {
    display("Button One clicked");
    return false;
  };
  handlers.button2 = function() {
    display("Button Two clicked");
    return false;
  };
  handlers.outerDiv = function() {
    display("Outer div clicked");
    return false;
  };
  handlers.innerDiv1 = function() {
    display("Inner div 1 clicked, not cancelling event");
  };
  handlers.innerDiv2 = function() {
    display("Inner div 2 clicked, cancelling event");
    return false;
  };

  function handleBodyClick(event) {
    var target, handler;

    event = event || window.event;
    target = event.target || event.srcElement;

    while (target && target !== this) {
      if (target.id) {
        handler = handlers[target.id];
        if (handler) {
          if (handler.call(this, event) === false) {
            if (event.preventDefault) {
              event.preventDefault();
            }
            return false;
          }
        }
      }
      else if (target.tagName === "P") {
        display("You clicked the message '" + target.innerHTML + "'");
      }
      target = target.parentNode;
    }
  }

  function display(msg) {
    var p = document.createElement('p');
    p.innerHTML = msg;
    document.body.appendChild(p);
  }

})();

Живой пример

Обратите внимание, как если вы щелкаете сообщения, которые динамически добавляются на страницу, ваш щелчок регистрируется и обрабатывается, даже если нет кода для привязки событий к добавляемым новым абзацам. Также обратите внимание, что ваши обработчики - это просто записи на карте, и у вас есть один обработчик, document.bodyкоторый выполняет всю отправку. Теперь вы, вероятно, укорените это в чем-то более целенаправленном document.body, но вы поняли идею. Кроме того, в приведенном выше примере мы в основном осуществляем рассылку id, но вы можете выполнить сопоставление настолько сложным или простым, насколько захотите.

Современные библиотеки JavaScript, такие как jQuery , Prototype , YUI , Closure или любые другие, должны предлагать функции делегирования событий, чтобы сгладить различия браузеров и аккуратно обрабатывать крайние случаи. JQuery , конечно же, с обеих его liveи delegateфункций, которые позволяют Вам определять обработчики , используя полный спектр CSS3 селекторов (и затем некоторые).

Например, вот эквивалентный код с использованием jQuery (за исключением того, что я уверен, что jQuery обрабатывает крайние случаи, а необработанная исходная версия выше не работает):

(function($) {

  $("#button1").live('click', function() {
    display("Button One clicked");
    return false;
  });
  $("#button2").live('click', function() {
    display("Button Two clicked");
    return false;
  });
  $("#outerDiv").live('click', function() {
    display("Outer div clicked");
    return false;
  });
  $("#innerDiv1").live('click', function() {
    display("Inner div 1 clicked, not cancelling event");
  });
  $("#innerDiv2").live('click', function() {
    display("Inner div 2 clicked, cancelling event");
    return false;
  });
  $("p").live('click', function() {
    display("You clicked the message '" + this.innerHTML + "'");
  });

  function display(msg) {
    $("<p>").html(msg).appendTo(document.body);
  }

})(jQuery);

Живая копия

TJ Crowder
источник
Я не хочу использовать Event.target, потому что это не всегда правильная цель. Вот так. Если вы нажмете на IMGвнутри a DIVи у DIVнего Event.targetбудет событие, будет IMGи нет никакого способа получить DIVобъект красиво. edit Btw I hate jQuery's .live'event'
Руди
@ Руди: Re event.target: То, что вы описываете, не является неправильным, это правильно. Если щелкнуть imgвнутри divи вы смотрите div, event.targetэто imgthisесть div). Взгляните еще раз на вышеизложенное. Вы можете прикрепить обработчик в любой точке цепочки между элементом, который был нажат ( imgнапример,), и элементом, который вы наблюдаете (в приведенном выше примере, полностью вниз document.body). Re live: Я предпочитаю delegateболее содержательную версию live. Я использовал liveвыше, потому что это было ближе всего к тому, что я сделал в необработанном примере. Best,
TJ Crowder
4

Возможно, вы захотите создать вспомогательную функцию, подобную этой:

function defineAndRun(name, func) {
    window[name] = func;
    func();
}

defineAndRun('addEventsAndStuff', function() {
    alert('oele');
});
pimvdb
источник
2
Почему это плохое решение? Потому что функции зарегистрированы в глобальном пространстве имен? Кто проголосовал за это?
Rudie
Отличная идея
:)
4

Ваш код содержит опечатку:

(function addEventsAndStuff() {
  alert('oele');
)/*typo here, should be }*/)();

так

(function addEventsAndStuff() {
  alert('oele');
 })();

работает. Ура!

[ править ] на основе комментария: и это должно запускаться и возвращать функцию за один раз:

var addEventsAndStuff = (
 function(){
  var addeventsandstuff =  function(){
    alert('oele');
  };
  addeventsandstuff();
  return addeventsandstuff;
 }()
);
KooiInc
источник
Глупые скобки все похожи !! Благодарность! Ура!
Rudie
Да ... Так что ... На самом деле это не работает. Функция выполняется немедленно, да, но она нигде не зарегистрирована, поэтому вызываю ее снова ->ReferenceError
Rudie
@Rudie: только что обнаружил, что KomodoEdit наконец-то делает все, что я всегда хотел, чтобы Aptana Studio делала (но постоянно ломалась), и, кроме того, он легко настраивается, быстр и упакован полезными дополнениями. И он выделяет совпадающие скобки: вы даже можете придать этим совпадающим скобкам ярко-красный предупреждающий цвет! Попробуйте, это бесплатно: activestate.com/komodo-edit
KooiInc
Чувак ... (И, очевидно, я набрал его в веб-редакторе SO, а не в настольном редакторе.)
Руди,
И это много лишнего набора текста и запоминания того, что и как печатать. Мне понравилось оригинальное (не работающее) решение, но новое слишком сложно =)
Руди
2

С ES6 еще проще:

var result = ((a, b) => `${a} ${b}`)('Hello','World')
// result = "Hello World"
var result2 = (a => a*2)(5)
// result2 = 10
var result3 = (concat_two = (a, b) => `${a} ${b}`)('Hello','World')
// result3 = "Hello World"
concat_two("My name", "is Foo")
// "My name is Foo"
Альберто
источник
Так как бы я назвал это снова? Как его зовут? Вы сохраняете только результат, а не функцию.
Руди
Этот метод используется в некоторых случаях, только если вам нужно вызвать его немедленно и вы не хотите запускать его повторно. В любом случае я отредактирую ответ, чтобы показать, как можно будет снова его вызвать, как вы думаете? @Rudie
Альберто
1
Мой вопрос касался названия и повторного запуска. concat_twoработает отлично, НО он всегда определяет его в глобальной области, поэтому он не будет работать с "use strict". Для меня это не важно, но это крошечный недостаток.
Руди
1

Если вы хотите создать функцию и немедленно выполнить -

// this will create as well as execute the function a()
(a=function a() {alert("test");})();

// this will execute the function a() i.e. alert("test")
a();
Правин Лобо
источник
1
Остерегайтесь, когда вы используете именованную функцию в качестве правого значения (как и выше), вы используете выражение именованной функции, которое, хотя и должно быть действительным, к сожалению, имеет проблемы в различных реализациях (в основном - quelle shock - IE ). Подробности: kangax.github.com/nfe
TJ Crowder
0

Попробуйте сделать так:

var addEventsAndStuff = (function(){
    var func = function(){
        alert('ole!');
    };
    func();
    return func;
})();
Александр Рулиов
источник
1
Это близко, но var foo = (function() {...})();вызывает функцию перед назначением. В скобках должно быть указано присвоение. (Вы хотите назначить, тогда звоните.)
Дэвид
Это также много набора текста И запоминания того, что вводить. Тогда я бы предпочел использовать свои текущие звонки.
Rudie