Какова цель самоисполняющейся функции в javascript?

427

В javascript, когда вы хотите использовать это:

(function(){
    //Bunch of code...
})();

через это:

//Bunch of code...
Ej.
источник
3
Также взгляните на ( техническое ) объяснение и здесь . Для синтаксиса, посмотрите, почему скобки необходимы и куда они должны идти .
Берги
Почему у него есть две последние скобки перед точкой с запятой?
Джонни
3
@johnny часть перед последними двумя круглыми скобками объявляет (анонимную) функцию. Эти две скобки вызывают функцию.
Эй.
7
«Немедленно вызванное выражение функции» или IIFE - лучшее название для этого.
Flimm

Ответы:

404

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

Например, как упоминалось в комментарии Александра :

(function() {
  var foo = 3;
  console.log(foo);
})();

console.log(foo);

Это сначала войдет в систему, 3а затем выдаст ошибку на следующем, console.logпотому что fooне определено.

Кен Браунинг
источник
7
А также для блага многих людей, включая целую кучу инженеров Netflix: ЭТО ПРОСТО ФУНКЦИЯ. Это само по себе не является представителем замыкания. Иногда автоматические вызовы используются в сочетании со сценариями, относящимися к закрытию, для выполнения аккуратных задач, но если вы не видите, что что-то удерживает ссылку, которая может быть собрана мусором и пропущена на незамкнутом языке, это не имеет ничего общего с чертовски делать с ЗАКРЫТИЯМИ.
Эрик Реппен
2
Значит, это в основном используется с закрытием?
Пир Абдул
@AlexanderBird, не совсем правильно ... если вы не var, как это: ...function(){ foo=3;}? Это установит глобальную переменную, верно?
Т.Тодуа
2
@AlexanderBird, но это уже происходит в локальных переменных внутри функций: function(){ var foo = 3; alert(foo); }; alert(foo);так что я до сих пор не понимаю
João Pimentel Ferreira
Ах, я понял, что вы сразу получаете все эти 3 функции: 1) вы выполняете функцию, только объявив ее; 2) Как и любая функция, область видимости переменных является только локальной; и 3) функция может быть анонимной, не загрязняющей основной области видимости
João Pimentel Ferreira
94

Упрощенные. Так выглядит очень нормально, это почти утешительно:

var userName = "Sean";

console.log(name());

function name() {
  return userName;
}

Тем не менее, что если я добавлю на свою страницу действительно удобную библиотеку JavaScript, которая переводит продвинутые символы в их представления базового уровня?

Чего ждать?

Я имею в виду, если кто-то печатает символ с каким-то акцентом на нем, но мне нужны только «английские» символы AZ в моей программе? Ну что ж ... Испанские символы «ñ» и французский «é» можно перевести в базовые символы «n» и «e».

Итак, кто-то хороший человек написал всеобъемлющий конвертер символов, который я могу включить в свой сайт ... Я включаю его.

Одна проблема: в нем есть функция с именем name, такая же, как и моя функция.

Это то, что называется столкновением. У нас есть две функции, объявленные в одной области с одним и тем же именем. Мы хотим избежать этого.

Поэтому нам нужно как-то расширить наш код.

Единственный способ использовать код в javascript - это обернуть его в функцию:

function main() {
  // We are now in our own sound-proofed room and the 
  // character-converter libarary's name() function can exist at the 
  // same time as ours. 

  var userName = "Sean";

  console.log(name());

  function name() {
    return userName;
  }
}

Это может решить нашу проблему. Теперь все закрыто и доступно только из наших открывающих и закрывающих скобок.

У нас есть функция в функции ... на которую странно смотреть, но она абсолютно легальна.

Только одна проблема. Наш код не работает. Наша переменная userName никогда не отображается в консоли!

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

function main() {
  // We are now in our own sound-proofed room and the 
  // character-converter libarary's name() function can exist at the 
  // same time as ours. 

  var userName = "Sean";

  console.log(name());

  function name() {
    return userName;
  }
}

main();

Или раньше!

main();

function main() {
  // We are now in our own sound-proofed room and the 
  // character-converter libarary's name() function can exist at the 
  // same time as ours. 

  var userName = "Sean";

  console.log(name());

  function name() {
    return userName;
  }
}

Второстепенная проблема: каковы шансы, что название «главный» еще не использовалось? ... так очень, очень стройный.

Нам нужно больше объема. И какой-то способ автоматически выполнить нашу функцию main ().

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

((){})();

Синтаксис неловкий как грех. Тем не менее, это работает.

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

Итак, давайте снова посмотрим на наш код, с некоторым самореализующимся синтаксисом:

(function main() {
  var userName = "Sean";

    console.log(name());

    function name() {
      return userName;
    }
  }
)();

Таким образом, в большинстве учебных пособий, которые вы читаете, вы теперь будете засыпаны термином «анонимный самореализация» или что-то подобное.

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

Когда что-то пойдет не так (и будет), вы будете проверять обратную трассировку в своем браузере. Это всегда легче сократить ваши вопросы кода при записи в трассировке стека имеют имена!

Очень скучный и, надеюсь, это поможет!

Шон Холден
источник
2
Спасибо :) Я искал в интернете все вокруг, пытаясь понять преимущества IIFE по сравнению с обычными функциями с точки зрения переменной конфиденциальности, и ваш ответ просто лучший. Все говорят, что одним из лучших преимуществ является то, что переменные и функции внутри IIFE являются «наконец-то» закрытыми, когда обычная функция просто дает вам то же самое. Наконец, я думаю, что я получил это через ваш процесс объяснения. В конце концов, IIFE - это просто функция, но теперь я понимаю, зачем ее использовать. Еще раз спасибо!
viery365 20.09.16
Спасибо, что нашли время, чтобы объяснить это так хорошо.
MSC
Хороший ответ. У меня есть вопрос по поводу вашего последнего замечания: когда вы рекомендуете называть все функции именами, вы говорите, что есть способ сделать это с помощью самозапускающихся функций, или предлагаете, чтобы все называли функцию, а затем вызывали ее? РЕДАКТИРОВАТЬ О, я вижу. Этот уже назван. Duh. Возможно, стоит отметить, что вы оправдываете использование именованной самореализующейся функции.
FireSBurnsmuP
Ну, друг мой, это тот ответ, который я искал:)
João Pimentel Ferreira
Это ответ, который я искал, а не тот, который помечен как принятый. Прав ли я, говоря, что речь идет не о конфликте имен переменных , а о столкновении имен функций ? Переменные даже в обычных не-iife-функциях локально ограничены и не сталкиваются с глобальными переменными, не так ли?
Кумар Маниш
32

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

Я большой поклонник :), потому что:

  • Это сводит код к минимуму
  • Обеспечивает отделение поведения от представления
  • Это обеспечивает закрытие, которое предотвращает конфликты имен

Чрезвычайно - (Почему вы должны сказать, что это хорошо?)

  • Речь идет об определении и выполнении функции одновременно.
  • Вы могли бы заставить эту самовыполняющуюся функцию возвращать значение и передавать функцию в качестве параметра другой функции.
  • Это хорошо для инкапсуляции.
  • Это также хорошо для определения объема блока.
  • Да, вы можете заключить все ваши файлы .js в самовоспроизводящуюся функцию и предотвратить глобальное загрязнение пространства имен. ;)

Больше здесь .

М.А. Хоссейн Тону
источник
43
Пункт 1. Как? Пункт 2. Это совершенно другая лучшая практика. Пункт 3. Какой функции нет? 4,5,6,7. Актуальность? 8. Ну, 1/8 неплохо, я думаю.
Эрик Реппен
2
Семь лет спустя, но для пункта 1. это вовсе не сокращает код, на самом деле он добавляет минимум две строки кода при создании функции.
YungGun
1
Единственный пункт здесь - «Это обеспечивает закрытие, которое предотвращает конфликты имен», любой другой пункт - перефразирование этого или ложное. может быть, вы можете упростить свой ответ?
pcarvalho
19

Пространства имен. Области применения JavaScript - на уровне функций.

Christoph
источник
7
отрицательные голоса все еще приходят, потому что я использовал пространство имен вместо области видимости ; это вопрос определения - см., например, Википедию : пространство имен в информатике (иногда также называемое областью имен) - это абстрактный контейнер или среда, созданная для хранения логической группы уникальных идентификаторов или символов (т. е. имен). и идентификатор пространства имен может предоставлять контекст (область действия в области компьютерных наук) имени, и эти термины иногда используются взаимозаменяемо.
Кристоф
5
Области уровня Javascript обеспечивают пространство, в котором находятся имена переменных, пространство имен ; то, что это аноним, не связанный с идентификатором пространства имен, не имеет значения ...
Кристоф
12

Я не могу поверить, что ни один из ответов не упоминает подразумеваемые глобалы.

(function(){})()Конструкция не защищает от подразумеваемых глобал, что для меня это большее беспокойства, см http://yuiblog.com/blog/2006/06/01/global-domination/

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

Более краткий var App = {}синтаксис обеспечивает аналогичный уровень защиты и может быть заключен в функциональный блок, когда он находится на «общедоступных» страницах. (см. Ember.js или SproutCore для реальных примеров библиотек, которые используют эту конструкцию)

Что касается privateсвойств, то они немного переоценены, если вы не создаете общедоступную среду или библиотеку, но если вам нужно реализовать их, у Дугласа Крокфорда есть несколько хороших идей.

Дэвид В. Кит
источник
8
Строгий режим защищает от неявных глобалов. Это в сочетании с авто-invoker бы вы покрыли. Я никогда не понимал шумиху над частной собственностью. Объявите переменные внутри конструктора func. Выполнено. Если мысль о том, что вы забыли использовать ключевое слово «new», не дает вам спать по ночам, напишите заводскую функцию. Сделано снова.
Эрик Реппен
8

Я прочитал все ответы, чего-то очень важного здесь не хватает , я поцелую. Есть две основные причины, по которым мне нужны самореализующиеся анонимные функции, или, лучше сказать, « Выражение с немедленным вызовом функции (IIFE) »:

  1. Лучшее управление пространством имен (предотвращение загрязнения пространства имен -> JS Module)
  2. Замыкания (имитация частных членов класса, как известно из ООП)

Первый был объяснен очень хорошо. Для второго, пожалуйста, изучите следующий пример:

var MyClosureObject = (function (){
  var MyName = 'Michael Jackson RIP';
  return {
    getMyName: function () { return MyName;},
    setMyName: function (name) { MyName = name}
  }
}());

Внимание 1: Мы не назначаем функцию MyClosureObject, более того, результат вызова этой функции . Будьте внимательны ()в последней строке.

Внимание 2: Что вам дополнительно нужно знать о функциях в Javascript, так это то, что внутренние функции получают доступ к параметрам и переменным функций, в которых они определены.

Давайте попробуем несколько экспериментов:

Я могу MyNameиспользовать, getMyNameи это работает:

 console.log(MyClosureObject.getMyName()); 
 // Michael Jackson RIP

Следующий оригинальный подход не сработает:

console.log(MyClosureObject.MyName); 
// undefined

Но я могу установить другое имя и получить ожидаемый результат:

MyClosureObject.setMyName('George Michael RIP');
console.log(MyClosureObject.getMyName()); 
// George Michael RIP

Редактировать: в приведенном выше примере MyClosureObjectпредназначен для использования без newпрефикса, поэтому по соглашению он не должен быть заглавными.

Одинокий, уединенный
источник
7

Есть ли параметр, и «Куча кода» возвращает функцию?

var a = function(x) { return function() { document.write(x); } }(something);

Закрытие. Значение somethingиспользуется функцией, назначенной для a. somethingможет иметь некоторое переменное значение (для цикла), и каждый раз, когда есть новая функция.

stesch
источник
+1; Я предпочитаю явную var x = something;во внешней функции xкак параметр, хотя: imo это более читабельно таким образом ...
Кристоф
@Christoph: Если значение «что-то» изменится после создания функции, оно будет использовать новое значение, а не то, которое было на момент ее создания.
Стеш
@stesch: откуда ты это взял? Насколько я знаю, это не так; единственный способ получить реальные ссылки в JS - использовать аргументы-объекты, но даже это не работает во всех браузерах
Кристоф
@Christoph: "JavaScript: The Good Parts", Дуглас Крокфорд (O'Reilly)
stesch
@stesch: это не работает так, как вы его описываете: новое значение будет использоваться, если вы отбросите переменную xи будете зависеть непосредственно от лексической области видимости, то есть document.write(something)...
Кристоф
6

Область изоляции, может быть. Так что переменные внутри объявления функции не загрязняют внешнее пространство имен.

Конечно, на половине реализаций JS они все равно будут.

хаос
источник
4
Что это за реализации?
Мэтью Крамли
1
Любая реализация, написанная не в строгом режиме и содержащая и неявное объявление var, которое делает его глобальным.
Эрик Реппен
5

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

for( var i = 0; i < 10; i++ ) {
  setTimeout(function(){
    console.log(i)
  })
}

Вывод: 10, 10, 10, 10, 10...

for( var i = 0; i < 10; i++ ) {
  (function(num){
    setTimeout(function(){
      console.log(num)
    })
  })(i)
}

Вывод: 0, 1, 2, 3, 4...

sg.cc
источник
Можете
С letвместо varпервого случая будет в порядке.
Виталий Зданевич
3

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

Guffa
источник
1

Поскольку функции в Javascript являются объектами первого класса, определяя его таким образом, он эффективно определяет «класс», очень похожий на C ++ или C #.

Эта функция может определять локальные переменные и иметь функции внутри нее. Внутренние функции (эффективные методы экземпляров) будут иметь доступ к локальным переменным (фактически переменные экземпляров), но они будут изолированы от остальной части сценария.

Джеймс Керран
источник
1

Самостоятельно вызванная функция в javascript:

Вызывающее себя выражение вызывается (запускается) автоматически, без вызова. Вызывающее себя выражение вызывается сразу после его создания. Это в основном используется для избежания конфликта имен, а также для достижения инкапсуляции. Переменные или объявленные объекты не доступны вне этой функции. Во избежание проблем минимизации (filename.min) всегда используйте самозапускаемую функцию.

Кишор Витекар
источник
1

Самоисполняющаяся функция используется для управления областью действия переменной.

Область действия переменной - это область вашей программы, в которой она определена.

Глобальная переменная имеет глобальную область видимости; он определяется везде в вашем коде JavaScript и может быть доступен из любого места скрипта, даже в ваших функциях. С другой стороны, переменные, объявленные внутри функции, определяются только в теле функции. Они являются локальными переменными, имеют локальную область видимости и доступны только внутри этой функции. Параметры функции также считаются локальными переменными и определяются только внутри тела функции.

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

var globalvar = "globalvar"; // this var can be accessed anywhere within the script

function scope() {
    alert(globalvar);
    localvar = "localvar" //can only be accessed within the function scope
}

scope(); 

Таким образом, в основном самовыполняющаяся функция позволяет писать код, не заботясь о том, как называются переменные в других блоках кода javascript.

Адехо Эммануэль IMM
источник
1
(function(){
    var foo = {
        name: 'bob'
    };
    console.log(foo.name); // bob
})();
console.log(foo.name); // Reference error

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

Основная цель обертывания функции с закрытыми и открытыми скобками - избежать загрязнения глобального пространства.

Переменные и функции внутри выражения функции стали частными (то есть) они не будут доступны вне функции.

Madhankumar
источник
1

Короткий ответ: предотвратить загрязнение Глобальной (или более высокой) области.

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

Это другой способ написать выражение IIFE. Я лично предпочитаю следующий метод:

void function() {
  console.log('boo!');
  // expected output: "boo!"
}();

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/void

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

Донован П
источник
Хорошо, я не видел такого использования voidраньше. Мне это нравится.
Эй.
1

Сначала вы должны посетить MDN IIFE , теперь некоторые моменты по этому поводу

  • это выражение для немедленного вызова функции . Поэтому, когда ваш файл JavaScript вызывается из HTML, эта функция вызывается немедленно.
  • Это предотвращает доступ к переменным в идиоме IIFE, а также загрязняет глобальную область.
Justin
источник
0

Похоже, что на этот вопрос все ответы уже готовы, но я все равно выложу свой вклад.

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

var myObject = {
    childObject: new function(){
        // bunch of code
    },
    objVar1: <value>,
    objVar2: <value>
}

Функция позволяет мне использовать дополнительный код для определения атрибутов и свойств childObjects для более чистого кода, таких как установка часто используемых переменных или выполнение математических уравнений; Ой! или проверка ошибок. в отличие от того, чтобы быть ограниченным синтаксисом создания вложенных объектов ...

object: {
    childObject: {
        childObject: {<value>, <value>, <value>}
    }, 
    objVar1: <value>,
    objVar2: <value>
}

У кодирования вообще есть много неясных способов делать много одинаковых вещей, заставляя задуматься: «Зачем беспокоиться?» Но появляются новые ситуации, когда вы больше не можете полагаться только на основные / основные принципы.

Garrett
источник
0

Учитывая ваш простой вопрос: «В javascript, когда вы хотите использовать это: ...»

Мне нравятся ответы @ken_browning и @ sean_holding, но вот еще один вариант использования, который я не вижу упомянутым:

let red_tree = new Node(10);

(async function () {
    for (let i = 0; i < 1000; i++) {
        await red_tree.insert(i);
    }
})();

console.log('----->red_tree.printInOrder():', red_tree.printInOrder());

где Node.insert - это какое-то асинхронное действие.

Я не могу просто вызвать await без ключевого слова async при объявлении моей функции, и мне не нужна именованная функция для последующего использования, но мне нужно дождаться этого вызова вставки или мне нужны другие более богатые функции (кто знает?) ,

zentechinc
источник
-3

IIRC позволяет создавать частные свойства и методы.

Олафур Вааге
источник