web-dev-qa-db-fra.com

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

Pourquoi est-il possible d'appeler une fonction en JavaScript comme celle-ci, testée 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 bug dans node.js, un bug dans le moteur V8, un comportement officiellement indéfini ou du JavaScript réellement valide pour tous les interprètes?

193
hyde

Semble être un Node REPL bug, mettant ces deux lignes dans un .js provoquera 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 ont ce correctif.

$ 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)
> 
84
leesei

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 la 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 Object littéraux/ initialiseurs plutôt que comme bloc .

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

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

Et, comme l'a mentionné leesei, cela a été changé pour 0.11.x, ce qui enveloppera simplement { ... } 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';
  }
201
Jonathan Lonowski

Un bug a été signalé il y a 4 mois, pour ce problème https://github.com/joyent/node/issues/5698

Et le problème était parce que, REPL entoure les instructions avec des parenthèses. Donc

foo)(

devient

(foo)()

Les explications réelles peuvent être trouvées ici https://github.com/joyent/node/issues/5698#issuecomment-19487718 .

60
thefourtheye