Чи можу я назвати функцію JavaScript і негайно її виконати?


91

У мене досить багато таких:

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

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

Повторне додавання подій необхідно, оскільки DOM змінився, тому раніше додані події зникли. Оскільки їх також слід прикріпити спочатку (duh), вони мають гарну функцію бути СУХИМИ.

У цьому налаштуванні немає нічого поганого (або він є?), Але чи можу я його трохи згладити? Я хотів би створити addEventsAndStuff()функцію і негайно викликати її, щоб вона не виглядала настільки аматорською.

Обидва нижче відповідають синтаксичною помилкою:

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

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

Хто взяв?


можливий дублікат функції самовиконання Javascript
Крістіан Санчес

2
Ні, @CristianSanchez.
Máxima Alekz

Імхо, ваш другий блок коду важко читати. У першому блоці коду кожному читачеві зрозуміло, що ви викликаєте функцію після init. Читачі, як правило, ігнорують закриваючі та перечитані дужки в кінці.
kaiser

Відповіді:


124

У прикладі, який ви опублікували у своєму запитанні, немає нічого поганого .. Інший спосіб зробити це може виглядати дивно, але:

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поверне функція? The function rчи the var r? Просто цікаво. Хороше рішення. Не обов’язково повинен бути один локатор =)
Руді

Я перейменував його, щоб бути більш чітким, але це return rбуло б те, function rяк це було, це сфера. Писав Скалу так постійно, намагаючись щось отримати на одній лінії.
Джеймс Горрі,

@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об'єкт красиво. редагувати Btw Я ненавиджу .live"подію" jQuery
Руді

@Rudie: Re event.target: Те, що ви описуєте, не неправильне, це правильне. Якщо клацнути imgвсередині a, divі ви спостерігаєте за div, event.targetє imgthisє div). Погляньте ще раз на вищезазначене. Ви можете приєднати обробник у будь-якій точці ланцюжка між елементом, по якому клацнули ( imgнаприклад,), та елементом, який ви переглядаєте (у наведеному вище, аж до document.body). Re live: Я переважно віддаю перевагу delegateбільш змістовній версії live. Я використовував liveвище, оскільки це було найближче до того, що я робив у необробленому прикладі. Найкраще,
TJ Crowder

4

Можливо, ви захочете створити допоміжну функцію наступним чином:

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

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

2
Чому це погане рішення? Оскільки функції зареєстровані у глобальному просторі імен? Хто проголосував за це?
Руді,

Чудова ідея
:)

4

Ваш код містить помилку:

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

тому

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

робіт. На здоров’я!

[ редагувати ] на основі коментаря: і це повинно запускатись і повертати функцію одним рухом:

var addEventsAndStuff = (
 function(){
  var addeventsandstuff =  function(){
    alert('oele');
  };
  addeventsandstuff();
  return addeventsandstuff;
 }()
);

Дурні дужки всі схожі !! Дякую! Насправді ура!
Руді,

Так ... Так, так ... Насправді це не працює. Функція виконується негайно, так, але вона ніде не зареєстрована, тому викликайте її знову ->ReferenceError
Руді

@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
Alberto

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() {...})();викликає функцію перед призначенням. Дужка повинна містити призначення. (Ви хочете призначити, а потім зателефонувати.)
Девід

Це також багато набору тексту і запам'ятовування того, що набирати. Тоді я б просто скористався своїми поточними дзвінками.
Руді,
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.