Pourquoi l'appel d'une fonction dans Node.js REPL avec) (fonctionne-t-il?

191

Pourquoi est-il possible d'appeler une fonction en JavaScript comme ceci, testé avec node.js:

~$ node
> function hi() { console.log("Hello, World!"); };
undefined
> hi
[Function: hi]
> hi()
Hello, World!
undefined
> hi)( // WTF?
Hello, World!
undefined
>

Pourquoi le dernier appel hi)(fonctionne-t-il? Est-ce un bogue dans node.js, un bogue dans le moteur V8, un comportement officiellement non défini ou un JavaScript réellement valide pour tous les interprètes?

Hyde
la source
1
reproductible dans nodejs v0.6.19 sur Ubuntu 13.04
mvp
1
un test rapide sur jsfiddle.net vous montrera qu'il s'agit d'un JavaScript invalide.
Christophe
6
Semble être un bogue de Node REPL, mettre les deux lignes dans un .jsprovoquera une erreur de syntaxe
leesei
8
Btw, crédit là où il est dû, cela est venu à irc (FreeNode #nodejs), par @miniml
hyde
3
Perl a quelque chose de similaire pour la même raison: perl -ne '$x += $_; }{ print $x'. Voir les fonctionnalités cachées de Perl
Adrian Pronk

Réponses:

84

Semble être un bogue Node REPL, mettre ces deux lignes dans un .jsprovoquera une erreur de syntaxe.

function hi() { console.log("Hello, World!"); }
hi)(

Erreur:

SyntaxError: Unexpected token )
    at Module._compile (module.js:439:25)
    at Object.Module._extensions..js (module.js:474:10)
    at Module.load (module.js:356:32)
    at Function.Module._load (module.js:312:12)
    at Function.Module.runMain (module.js:497:10)
    at startup (node.js:119:16)
    at node.js:901:3

Problème soumis # 6634 .

Reproduit sur v0.10.20.


v0.11.7 a corrigé ce problème.

$ nvm run 0.11.7
Running node v0.11.7
> function hi() { console.log("Hello, World!"); }
undefined
>  hi)(
SyntaxError: Unexpected token )
    at Object.exports.createScript (vm.js:44:10)
    at REPLServer.defaultEval (repl.js:117:23)
    at REPLServer.b [as eval] (domain.js:251:18)
    at Interface.<anonymous> (repl.js:277:12)
    at Interface.EventEmitter.emit (events.js:103:17)
    at Interface._onLine (readline.js:194:10)
    at Interface._line (readline.js:523:8)
    at Interface._ttyWrite (readline.js:798:14)
    at ReadStream.onkeypress (readline.js:98:10)
    at ReadStream.EventEmitter.emit (events.js:106:17)
> 
leesei
la source
27
Ils sont effectivement allés de l'avant et l'ont réparé? Awwww, dommage, j'aimerais vraiment le voir démarrer une culture et devenir une fonctionnalité dans toutes les langues. Combien de fois ai-je tapé) (au lieu de () pressé ... :))
geomagas
18
@geomagas Vous pensez function a)arg1, arg2( } ]arg2 + arg1[ return; {que la syntaxe devrait être valide?
azz
40
Non, pas vraiment. En fait, c'était une blague.
geomagas
7
Il était une fois une implémentation Lisp avec une option DWIM qui corrigeait automatiquement les fautes d'orthographe et autres erreurs mineures. en.wikipedia.org/wiki/DWIM
Barmar
2
@geomagas, eh bien, certains sont déjà allés de l'avant et y ont pensé - npma install et isntall . pariez que vous n'avez pas remarqué :)
Eliran Malka
201

Cela est dû à la façon dont le REPL évalue l'entrée, qui est finalement comme:

(hi)()

Les parenthèses supplémentaires sont ajoutées pour le forcer à être une expression :

  // First we attempt to eval as expression with parens.
  // This catches '{a : 1}' properly.
  self.eval('(' + evalCmd + ')',
      // ...

L'intention est de traiter {...}comme des Objectlittéraux / initialiseurs plutôt que comme un bloc .

var stmt = '{ "foo": "bar" }';
var expr = '(' + stmt + ')';

console.log(eval(expr)); // Object {foo: "bar"}
console.log(eval(stmt)); // SyntaxError: Unexpected token :

Et, comme leesei l'a mentionné, cela a été changé pour 0.11.x, qui va simplement envelopper{ ... } plutôt que toutes les entrées:

  if (/^\s*\{/.test(evalCmd) && /\}\s*$/.test(evalCmd)) {
    // It's confusing for `{ a : 1 }` to be interpreted as a block
    // statement rather than an object literal.  So, we first try
    // to wrap it in parentheses, so that it will be interpreted as
    // an expression.
    evalCmd = '(' + evalCmd + ')\n';
  } else {
    // otherwise we just append a \n so that it will be either
    // terminated, or continued onto the next expression if it's an
    // unexpected end of input.
    evalCmd = evalCmd + '\n';
  }
Jonathan Lonowski
la source
19
Cela signifie-t-il que cela hi)(argfonctionnera? Cela pourrait être abusé pour écrire du code vraiment WTF ;-)
Doctor Jones
Je ne comprends toujours pas pourquoi cela fonctionnerait. Ne ferait-il pas une erreur de syntaxe à cause du paren ouvert inégalé?
Peter Olson
2
hi)(argdevient (hi)(arg)- rien d'inégalé
SheetJS