5 votes

Une énigme à propos de ce/@ en Javascript/Coffeescript

Je travaille sur le livre de Trevor Burnham. CoffeeScript et je suis tombé sur une étrange énigme concernant this / @ . L'énigme comporte plusieurs parties (et je suis peut-être tout simplement très confus), je vais donc essayer d'être aussi clair que possible.

Le principal problème que j'ai est que j'obtiens des résultats variés et incohérents en cours d'exécution. le même à travers différents REPLs et interpréteurs. Je teste avec (1) le coffee REPL et interprète, (2) REPL et interprète de Node et (3) REPL et interprète de v8.

Voici le code, d'abord en Coffeescript puis en Javascript :

// coffeescript
setName = (name) -> @name = name

setName 'Lulu'
console.log name
console.log @name

// Javascript via the coffee compiler
(function() {
  var setName;
  setName = function(name) {
    return this.name = name;
  };
  setName('Lulu');
  // console.log for node below - print for v8
  // uncomment one or the other depending on what you're trying
  // console.log(name);
  // console.log(this.name);
  // print(name);
  // print(this.name);
}).call(this);

Voici les résultats :

$ coffee setName.coffee
Lulu
undefined

# coffee REPL
# This appears to be a bug in the REPL
# See https://github.com/jashkenas/coffee-script/issues/1444
coffee> setName = (name) -> @name = name
[Function]
coffee> setName 'Lulu'
'Lulu'
coffee> console.log name
ReferenceError: name is not defined
    at repl:2:1
    at Object.eval (/Users/telemachus/local/node-v0.4.8/lib/node_modules/coffee-script/lib/coffee-script.js:89:15)
    at Interface.<anonymous> (/Users/telemachus/local/node-v0.4.8/lib/node_modules/coffee-script/lib/repl.js:39:28)
    at Interface.emit (events.js:64:17)
    at Interface._onLine (readline.js:153:10)
    at Interface._line (readline.js:408:8)
    at Interface._ttyWrite (readline.js:585:14)
    at ReadStream.<anonymous> (readline.js:73:12)
    at ReadStream.emit (events.js:81:20)
    at ReadStream._emitKey (tty_posix.js:307:10)

coffee> console.log @name
undefined

$ v8 setName.js
Lulu
Lulu

# v8 REPL
>> (function(){var setName; setName=function(name){return this.name=name;};setName('Lulu');print(name);print(this.name);}).call(this);
Lulu
Lulu

# Switch print to console.log or require puts from sys
$ node setName.js
Lulu
undefined

# node REPL
> (function() {
...   var setName;
...   setName = function(name) {
...     return this.name = name;
...   };
...   setName('Lulu');
...    console.log(name);
...    console.log(this.name);
... }).call(this);
Lulu
Lulu

Les vraies questions, je suppose, sont donc (1) quels résultats dois-je attendre et (2) pourquoi ces interprètes et REPL ne peuvent-ils pas s'entendre ? (Ma théorie actuelle est que v8 a raison : dans le contexte global name y this.name devrait être la même chose, j'aurais pensé. Mais je suis tout à fait prêt à croire que je ne comprends pas this en Javascript).

Editar : Si j'ajoute this.name = null / @name = null avant d'appeler setName (comme le suggère Pointy ci-dessous) alors Coffeescript et Node me renvoient 'Lulu' et 'null' mais la v8 renvoie toujours 'Lulu' pour les deux. (La v8 est toujours plus logique pour moi ici. J'ai mis name a null initialement dans le contexte global, mais ensuite setName le définit (dans le contexte global) comme "Lulu". Donc après, c'est ce que je devrais voir là).

9voto

Trevor Burnham Points 43199

Donc, tout d'abord, il y a un bug avec le CoffeeScript REPL, numéro 1444 que j'ai signalée après que Telemachus l'ait portée à mon attention.

Mais la question la plus intéressante ici (et une que je dois noter dans mon livre CoffeeScript ) est que this dans la portée la plus externe d'un module Node.js n'est pas global -c'est que ce module exports . Essayez ceci :

console.log this is exports
console.log do -> this is global

Vous constaterez que les deux déclarations sont évaluées à true lorsque vous exécutez ce code dans un module Node. C'est pourquoi name y @name évaluent à des choses différentes : name par lui-même pointera toujours vers global.name à moins que ce ne soit dans le cadre d'un projet de l'UE. var name déclaration ; mais @name ne fera que pointer vers global.name dans une fonction appelée dans le global (par défaut). Dans un module Node.js, en dehors de toute fonction, il pointera sur exports.name .

2voto

Pointy Points 172438

Je ne sais pas pourquoi vous obtenez des résultats différents, mais je sais que l'invocation d'une fonction implique implicitement la mise en place de this . La fonction interne "setName()" possède donc sa propre fonction this indépendant de la valeur de this dans la fonction externe dans laquelle elle est définie. Ainsi, le fait de définir this via l'invocation de ".call()" n'a aucun effet sur l'interface de l'utilisateur. this dans la fonction interne "setName()", car lorsque "setName()" est appelée, aucun récepteur n'est impliqué.

1voto

Borgar Points 12493

Je ne connais pas beaucoup CoffeeScript mais pas mal JavaScript, je ne peux donc tenter d'expliquer cela que du point de vue de ce que fait (ou devrait faire) le code compilé :

(function(){
  // In here "this" === [global]
  //
  // The purpose of this wrapper pattern is that it causes "this" to be
  // the global object but all var declared variables will still be 
  // scoped by the function.

  var ctx = this;     // let's keep test a ref to current context

  var setName;
  setName = function(name) {
    console.log(this === ctx);   // !! true !!

    // Because in here "this" is the global context/object
    // this is setting [global].name = name
    return this.name = name;
  };

  setName('Lulu');         // It's context will be [global]

  console.log(name);       // (name === [global].name) == true
  console.log(this.name);  // (this.name === [global].name) == true

}).call(this);

Ce qui se passe (ou devrait se passer) est effectivement ceci (en supposant que le navigateur où global est window ) :

(function() {
  var setName;
  setName = function(name) {
    return window.name = name;
  };
  setName('Lulu');
  console.log(window.name);   // 'Lulu'
  console.log(window.name);   // 'Lulu'
}).call(this);

Alors, pourquoi ça ne correspond pas entre les moteurs ?

Parce que les différents environnements utilisent des moyens différents pour vous remettre l'objet global et gérer le scoping. Il est difficile de le dire avec certitude et chaque environnement peut avoir une raison distincte pour son comportement. Cela dépend beaucoup de la façon dont ils évaluent le code (en supposant qu'aucun des moteurs ne présente de bogues).

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X