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


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*/}()

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

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 символов (,)мы просто использовали один символ!


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

5

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

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

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

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

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

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

Это неверно !изменяет способ, которым среда выполнения анализирует функцию. Это заставляет среду выполнения рассматривать функцию как выражение функции (а не как объявление). Это позволяет разработчику немедленно вызывать функцию с использованием ()синтаксиса. !также будет применять себя (то есть отрицание) к результату вызова выражения функции.
Бен Астон

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