Могу ли я назвать функцию 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');
})();

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



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);

Живая копия


Я не хочу использовать 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это img(и thisесть 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');
});

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;
 }()
);

Глупые скобки все похожи !! Благодарность! Ура!
— 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
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.