Что делает восклицательный знак перед функцией?

1242
!function () {}();
Себастьян Отто
источник
4
@Dykam Его полезность объясняется в этом ответе: stackoverflow.com/questions/5422585/…
hectorct
7
@befzz Лучше называть это «выражением немедленного вызова функции», как позже объясняется в этой статье («самореализация» подразумевает рекурсию)
Зак Эспозито

Ответы:

2119

Синтаксис JavaScript 101. Вот объявление функции :

function foo() {}

Обратите внимание, что нет точки с запятой: это просто объявление функции . Вам потребуется вызов foo(), чтобы фактически запустить функцию.

Теперь, когда мы добавляем, казалось бы, безобидный восклицательный знак: !function foo() {}он превращается в выражение . Теперь это выражение функции .

!В одиночку не вызывает функцию, конечно, но теперь мы можем поставить ()в конце: !function foo() {}()который имеет более высокий приоритет , чем !и мгновенно вызывает функцию.

Так что автор делает сохранение байта на выражение функции; более читабельный способ написать это будет так:

(function(){})();

Наконец, !возвращает выражение true. Это происходит потому , что по умолчанию все IIFE возврат undefined, который оставляет нам !undefinedчто true. Не особенно полезно.

Нил
источник
230
+1. Это действительно лучший ответ здесь, и, к сожалению, вряд ли проголосовали. Очевидно, что !возвращает логическое значение, мы все это знаем, но вы замечаете, что он также преобразует оператор объявления функции в выражение функции, чтобы можно было немедленно вызывать функцию, не заключая ее в скобки. Не очевидно, а ясно намерение кодера.
gilly3
65
+1 Это единственный ответ, который на самом деле касается того, ПОЧЕМУ вы хотели бы сделать это, и почему кто-то видит, что он использовал больше, чем могло показаться оправданным отрицанием результата возврата. Одинарный оператор! (также ~, - и +) устраняет неоднозначность из объявления функции и позволяет родителям в конце () вызывать функцию на месте. Это часто делается для создания локальной области видимости / пространства имен для переменных при написании модульного кода.
Том Ожер
66
Это еще одно преимущество! вызывает вставку точек с запятой, поэтому невозможно, чтобы эта версия была неправильно соединена с файлом, который не заканчивается на;; Если у вас есть форма (), она будет считать это вызовом функции, которая была определена в предыдущем файле. Кончик шляпы моему коллеге.
Юре Триглав
5
@Carnix устраняет var foo =неоднозначность выражений / выражений, и вы можете просто написать var foo = function(bar){}("baz");и т. Д.
Нил
6
Обычно это делается с помощью сценариев минификации / углификации, где учитывается каждый байт.
Дима Сливин
367

Функция:

function () {}

ничего не возвращает (или не определено).

Иногда мы хотим вызвать функцию прямо при ее создании. Вы можете попробовать это:

function () {}()

но это приводит к SyntaxError.

Использование !оператора перед функцией вызывает его как выражение, поэтому мы можем вызвать его:

!function () {}()

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

(function () {})()
Майкл Берр
источник
28
кому это может понадобиться?
Андрей
13
это единственный ответ, который объясняет случай в вопросе, браво!
Андрей
14
Ваш второй пример кода не является допустимым JavaScript. Цель состоит в том !, чтобы превратить объявление функции в выражение функции, вот и все.
Скиллдрик
8
@Andrey Твиттер начальной загрузки использует это во всех имеющихся там файлах плагинов javascript (jQuery). Добавление этого комментария на тот случай, если у других может возникнуть такой же вопрос.
Anmol Saraf
2
d3.js также использует !functionсинтаксис
Кристиан
65

Есть хорошая точка для использования !для вызова функций, отмеченная в руководстве по airbnb JavaScript

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

!function abc(){}();
!function bca(){}();

Будет работать так же, как

!function abc(){}();
(function bca(){})();

но сохраняет один символ и выглядит произвольно лучше.

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

abcval = !function abc(){return true;}() // abcval equals false
bcaval = +function bca(){return true;}() // bcaval equals 1
zyxval = -function zyx(){return true;}() // zyxval equals -1
xyzval = ~function xyz(){return true;}() // your guess?

но если вы используете шаблоны IIFE для одного файла с разделением кода на один модуль и используете инструмент concat для оптимизации (что делает одну строку одним файлом), то построение

!function abc(/*no returns*/) {}()
+function bca() {/*no returns*/}()

Будет выполнять безопасное выполнение кода, так же, как самый первый пример кода.

Этот вызовет ошибку, поскольку JavaScript ASI не сможет выполнить свою работу.

!function abc(/*no returns*/) {}()
(function bca() {/*no returns*/})()

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

Это работает:

!function abc(/*no returns*/) {}()
^function bca() {/*no returns*/}()

Это не:

^function abc(/*no returns*/) {}()
!function bca() {/*no returns*/}()
dmi3y
источник
3
На самом деле, эти другие символы не имеют того же эффекта. Да, они позволяют вам вызывать функцию, как описано, но они не идентичны. Обратите внимание: var foo =! Function (bar) {console.debug (bar); }("летучая мышь"); Независимо от того, какой из ваших символов вы ставите перед собой, вы получаете «летучую мышь» в своей консоли. Теперь добавьте console.debug ("foo:", foo); - вы получите очень разные результаты в зависимости от того, какой символ вы используете. ! вызывает возвращаемое значение, которое не всегда желательно. Я предпочитаю синтаксис ({}) () для ясности и точности.
Carnix
29

Возвращает, может ли оператор оценить как ложный. например:

!false      // true
!true       // false
!isValid()  // is not valid

Вы можете использовать его дважды, чтобы привести значение к логическому:

!!1    // true
!!0    // false

Итак, чтобы более прямо ответить на ваш вопрос:

var myVar = !function(){ return false; }();  // myVar contains true

Редактировать: имеет побочный эффект изменения объявления функции на выражение функции. Например, следующий код недопустим, поскольку он интерпретируется как объявление функции, в котором отсутствует требуемый идентификатор (или имя функции ):

function () { return false; }();  // syntax error
gilly3
источник
6
Для ясности для читателей, которые могут захотеть использовать присваивание с немедленно вызванной функцией, ваш пример кода var myVar = !function(){ return false; }()может опустить !подобное, var myVar = function(){ return false; }()и функция будет выполнена правильно, а возвращаемое значение останется нетронутым.
Марк Фокс
1
Чтобы быть понятным, вы можете использовать его один раз, чтобы привести к логическому, потому что это логический оператор не . ! 0 = истина и! 1 = ложь. Для целей JavaScript минификации, вы хотите заменить trueс !0и falseс !1. Сохраняет 2 или 3 символа.
Трийнко
9

Это просто, чтобы сохранить байт данных, когда мы делаем минификацию javascript.

рассмотрим ниже анонимную функцию

function (){}

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

(function (){}())

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

!function (){}()

Тем не менее, обе являются функциями, вызывающими себя, и мы также сохраняем байт. Вместо 2 символов (,)мы просто использовали один символ!

Varatharaj
источник
1
Это полезно, потому что часто вы будете видеть это в минимизированных js
для имени
5

! это логический оператор НЕ , это логический оператор, который будет инвертировать что-то в противоположность.

Хотя вы можете обойти скобки вызванной функции, используя BANG (!) Перед функцией, она все равно инвертирует возвращаемое значение, что может быть не тем, что вы хотели. Как и в случае IEFE, он возвращает неопределенное значение , которое при обращении становится логическим значением true.

Вместо этого используйте закрывающую скобку и BANG ( ! ), Если это необходимо.

// I'm going to leave the closing () in all examples as invoking the function with just ! and () takes away from what's happening.

(function(){ return false; }());
=> false

!(function(){ return false; }());
=> true

!!(function(){ return false; }());
=> false

!!!(function(){ return false; }());
=> true

Другие операторы, которые работают ...

+(function(){ return false; }());
=> 0

-(function(){ return false; }());
=> -0

~(function(){ return false; }());
=> -1

Комбинированные операторы ...

+!(function(){ return false; }());
=> 1

-!(function(){ return false; }());
=> -1

!+(function(){ return false; }());
=> true

!-(function(){ return false; }());
=> true

~!(function(){ return false; }());
=> -2

~!!(function(){ return false; }());
=> -1

+~(function(){ return false; }());
+> -1
SoEzPz
источник
5

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

!function bool() { return false; }() // true
!function bool() { return true; }() // false

Пропуск !в приведенных выше примерах будет SyntaxError .

function bool() { return true; }() // SyntaxError

Тем не менее, лучший способ достичь этого:

(function bool() { return true; })() // true
oozzal
источник
Это неверно !изменяет способ, которым среда выполнения анализирует функцию. Это заставляет среду выполнения рассматривать функцию как выражение функции (а не как объявление). Это позволяет разработчику немедленно вызывать функцию с использованием ()синтаксиса. !также будет применять себя (то есть отрицание) к результату вызова выражения функции.
Бен Астон
3

Это еще один способ написания IIFE (выражение, вызываемое немедленно).

Другой способ написания -

(function( args ) {})()

такой же как

!function ( args ) {}();
Камаль
источник
Ну, это не совсем то же самое; 2-ая ​​форма отрицает результат вызова функции (и затем выбрасывает его, потому что нет присвоения значения). Я бы строго предпочел более явный (function (args) {...})()синтаксис и оставил эту !functionформу инструментам минимизации и запутывания.
Тобиас
-1

! будет отрицать (напротив) все, что вы ожидаете в результате, т.е. если у вас есть

var boy = true;
undefined
boy
true
!boy
false

когда вы звоните boy, ваш результат будет true, но в тот момент, !когда вы добавите при вызове boy, т. е. !boyваш результат будет false. Который другими словами вы имеете в виду NotBoy , но на этот раз это в основном логический результат, либо trueили false.

Это то же самое, что происходит с !function () {}();выражением: запуск только function () {}();помечает вас как ошибку, но добавляет !прямо перед function () {}();выражением, делает его противоположным тому, function () {}();которое должно вернуть вас true. Пример можно увидеть ниже:

function () {}();
SyntaxError: function statement requires a name
!function () {}();
true
antzshrek
источник