ES6 немедленно вызвал функцию стрелки

149

Почему это работает в Node.jsконсоли (протестировано в 4.1.1 и 5.3.0), но не работает в браузере (протестировано в Chrome)? Этот блок кода должен создавать и вызывать анонимную функцию, которая ведет журнал Ok.

() => {
  console.log('Ok');
}()

Кроме того, хотя вышеперечисленное работает в Node, это не работает:

n => {
  console.log('Ok');
}()

Ни это:

(n) => {
  console.log('Ok');
}()

Странно то, что при добавлении параметра он на самом деле бросает SyntaxErrorв немедленно вызываемую часть.

XCS
источник
8
Хороший вопрос. Обе параметризованные версии работают с Babel
CodingIntrigue
2
Из интереса (n => { console.log("Ok"); })();работает?
CodingIntrigue
Да, (n => { console.log("Ok"); })()работает даже в консоли Chrome Dev
XCS
и так, спустя 3 года ответ? конечно, один из 3 ответов ниже должен быть принят ?!
joedotnot
@joedotnot Я не получил четкого ответа, в основном это была странная реализация в Node.js. Похоже, в последней версии Node.jsпервая версия больше не работает.
XCS

Ответы:

194

Вам нужно сделать это выражение функции вместо определения функции, которое не требует имени и делает его допустимым JavaScript.

(() => {
  console.log('Ok');
})()

Является эквивалентом IIFE

(function(){
   console.log('Ok')
})();

И возможная причина, по которой это работает в Node.js, но не в chrome, заключается в том, что его синтаксический анализатор интерпретирует его как функцию, выполняемую самостоятельно, так как

function() { console.log('hello'); }();

Node.jsЭто нормально работает в выражениях функций и Chrome и Firefox, и большинство браузеров интерпретирует это так. Вы должны вызвать его вручную.

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

Что касается параметризованной версии , это будет работать.

((n) => {
  console.log('Ok');
})()
недействительным
источник
4
Первый пример работает Node.jsи фактически записывает значение. Мой вопрос: почему это работает? А почему нет, когда я добавляю параметр?
XCS
1
Я довольно хорошо знаком с IIFEs и знаю, как исправить мой код. Мне было просто любопытно, почему, например, мой IIFEне работает при nдобавлении параметра, даже если он работал без параметра.
XCS
3
Я не понизил голос, но вопрос в том, почему параметризованная версия не работает в Node, когда точно такое же определение без параметра - это не вопрос различий между реализациями анонимных функций Node / Chrome
CodingIntrigue
1
Это хорошо известно, но он не отвечает на вопрос, как упоминалось ранее, - почему параметризованная версия не работает в Node, когда точно такое же определение без параметра
jkris
Но что эквивалентно function(){}()в функциях стрелок? Если я хочу, чтобы объект функции выставлял выражения функций от этого.
Дабадаба
18

Ничто из этого не должно работать без скобок.

Зачем?

Потому что согласно спецификации:

  1. ArrowFunction находится в списке под ВыраженияПрисваиванием
  2. В LHS из в значении вложенного ВыраженияCall должна быть MemberExpression , SuperCall или значение вложенного ВыраженияCall

Таким образом, ArrowFunction не может быть на LHS CallExpression .


То , что это фактически означает , в том , как =>следует интерпретировать, что он работает на такой же уровень , как операторы присваивания =, и +=т.д.

Смысл

  • x => {foo}() не становится(x => {foo})()
  • Переводчик пытается интерпретировать это как x => ({foo}())
  • Таким образом, это все еще ошибка синтаксиса
  • Таким образом, интерпретатор решает, что (должно быть неправильно и выдает SyntaxError

Существовал ошибка на Бабеля об этом здесь тоже.

Пол С.
источник
Это некоторые верные моменты, но если я попытаюсь заменить первую рабочую версию на: () => ({console.log('Ok')}())она больше не работает. Так что на самом деле это не так.
XCS
@Cristy Это недопустимое производство функции Arrow. Он думает, что вы пытаетесь создать объект с литералом объекта (заключенным в скобки) и console.log(...)не является допустимым именем ключа.
thefourtheye
@Cristy: Да, я думаю, что часть интерпретации выше (бит «Значение») может быть не совсем правильной, но детали спецификации, насколько я могу судить. Это также соответствует ошибке, которую я получаю от V8: SyntaxError: Unexpected token ((указывая на (in ()в конце, а не (in console.log(...)).
TJ Crowder
@TJCrowder вы правы, я вычеркну это, так как это изменяет сообщение об ошибке, и то, что я пытаюсь сказать, не передается (то есть (причина, по которой переводчик сдался после изнурительных попыток найти правильную интерпретацию и уходит «тогда это должно быть неправильно»), что может быть ошибочным в любом случае, потому что я не знаю, как на самом деле написан переводчик
Пол С.
Мне интересно, не является ли это действительным токеном в этой позиции, не попытается ли он вставить точку с запятой?
thefourtheye
2

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

> 3 + 2
< 5

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

Это также одна из причин того, что некоторые чистые коды ES6 в скриптах работают нормально, но не в консоли Chrome Dev Tools.

Попробуйте выполнить это в консоли Node и Chrome:

{ let a = 3 }

В Node или <script>теге он работает просто отлично, но в консоли он дает Uncaught SyntaxError: Unexpected identifier. Он также дает вам ссылку на источник, в форме VMxxx:1которого вы можете щелкнуть, чтобы проверить оцениваемый источник, который отображается как:

({ let a = 3 })

Так почему он это сделал?

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

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

Вот хороший пример чего-то очень похожего:

https://stackoverflow.com/a/28431346/46588

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

Вкратце: консоль пытается максимально точно имитировать глобальный контекст выполнения, но из-за ограничений взаимодействия с движком v8 и семантики JavaScript это иногда трудно или невозможно решить.

Клемен Славич
источник
1
В этом весь смысл, я забочусь о параметре, но он не работает с набором параметров.
XCS
Хорошо, я понимаю вашу точку зрения. Разница в том, как консоль Chrome Dev Tools фактически выполняет ваш код. Я отредактирую ответ, чтобы отразить это.
Клемен Славич
0

Я задал вопрос, как это:

@getify У меня такой вопрос: чтобы создать шаблон #IIFE, мы используем parans вокруг объявления функции, чтобы преобразовать его в выражение функции и затем вызвать его. Теперь в функции стрелки IIFEs, зачем нам паран ?! Разве функция стрелки уже не является выражением по умолчанию ?!

и это ответ Кайла Симпсона:

функция стрелки - это expr, но нам нужно окружить парены b / c «приоритета оператора» (sorta), чтобы последние парены для вызова стрелки-IIFE применялись ко всей функции, а не только к последнему токену ее тела ,

x => console.log(x)(4)

против

(x => console.log(x))(4)

- getify (@getify) 12 июня 2020 г.

Эршад Кадери
источник
У меня был вопрос, почему это работает на некоторых компиляторах, а не на других.
XCS
Это потому, что разные компиляторы ведут себя по-разному в некоторых деталях, как разные браузеры, которые, конечно, имеют разные компиляторы
Ershad Qaderi
Вы правы, они ведут себя по-разному, но спецификации JavaScript одинаковы для всех из них. Мне было любопытно, какой из них был прав, что спецификация JS говорит об этом случае и особенно, как могло случиться так, что это работает без аргумента, но не с аргументом. Я искал более технический ответ.
XCS
Ваш пример довольно очевиден, в первом случае он действительно должен вызывать console.log(x)(4).
XCS
Я просто догадываюсь здесь, но я думаю, что было бы очень разумно объяснить это так: в выражениях функций стрелок, когда мы не используем параметр, мы должны использовать парены, и для движка очень ясно, что это стрелка Выражение функции, но когда у нас есть один параметр, пареноны произвольны, что может быть не совсем понятно, что это функция и сбивает с толку тот механизм, чтобы разрешить путаницу, мы должны поставить пару скобок вокруг целого выражения функции
Эршад Кадери