2016-04-25 6 views
3

Dans un cours en ligne, Kyle Simpson dit que le code suivant démontre la nécessité de hisser en javascript, car sans lever «l'une des fonctions serait toujours déclarée trop tard».Est-ce que le hissage est vraiment nécessaire en javascript pour permettre une récursion mutuelle?

a(1) // 39 

function a(foo){ 
    if (foo > 20) return foo 
    return b(foo+2) 
} 

function b(foo){ 
    return c(foo) + 1 
} 

function c(foo){ 
    return a(foo*2) 
} 

Mais cela fonctionne très bien.

var a = function(foo){ 
    if (foo > 20) return foo 
    return b(foo+2) 
} 

var b = function(foo){ 
    return c(foo) + 1 
} 

var c = function(foo){ 
    return a(foo*2) 
} 

a(1) // 39 

Alors, quelle est l'histoire? Commodité et le placement de l'invocation de côté, y at-il des situations que exigent de levage?

+0

la seconde donnera une erreur si 'a (1)' est appelé avant les définitions de fonction. – gurvinder372

+0

Oui, mais c'est trivialement vrai de toute fonction et son invocation (comme je le note dans la question). L'affirmation de Simpson semble être que la structure mutuellement récursive des fonctions nécessite un hissage, mais cela ne semble pas être le cas. – rswerve

+0

Dans le premier, si vous donnez 'a (1)' avant la définition de la fonction, il ne donnera pas la même erreur. – gurvinder372

Répondre

1

L'affirmation que j'ai faite à propos d'un JS non hissé incapable de prendre en charge la récurrence mutuelle est juste une conjecture à des fins d'illustration. Il est conçu pour aider à comprendre la nécessité pour le langage de connaître les variables disponibles dans le (s) domaine (s). Ce n'est pas une prescription pour un comportement linguistique exact. Une caractéristique de langage comme le levage - le fait de lever n'existe pas, c'est juste une métaphore pour les variables qui sont déclarées dans les environnements de portée à l'avance pendant la compilation, avant l'exécution - est une caractéristique tellement fondamentale qu'elle ne peut pas facilement être raisonnés lorsqu'ils sont séparés du reste des caractéristiques de la langue.

De plus, il est impossible de tester complètement cette hypothèse dans JS seulement. L'extrait de l'OP ne traite qu'une partie de l'équation, c'est-à-dire qu'elle utilise des expressions de fonction au lieu de déclarations de fonction pour éviter le levage de fonction. Le langage que j'utilisais pour comparer à l'illustration est C, qui exige par exemple que les signatures de fonction soient déclarées dans les fichiers d'en-tête .hh afin que le compilateur sache à quoi ressemble une fonction même si elle n'a pas "vu" encore. Sans cela, le compilateur étouffe. C'est une sorte de levage manuel dans un sens. C le fait pour la vérification de type, mais on peut imaginer que ce genre d'exigence existe pour d'autres raisons que cela.


Une autre façon de penser à ce sujet est de savoir si JS est un langage compilé où tout a été découvert avant qu'il exécute, ou si elle est interprété haut vers le bas en une seule passe. Si JS était interprété de manière descendante, et qu'il atteignait la définition d'une fonction a() qui faisait référence à un b() qu'il n'avait pas encore vu, cela pourrait poser problème. Si cette expression d'appel était traitée de manière non paresseuse, le moteur ne pouvait pas comprendre à ce moment-là à quoi servirait l'appel b(), car b() n'avait pas encore été traité. Certaines langues sont paresseuses et certaines sont non paresseuses.

En l'état, JS est compilé avant l'exécution, de sorte que le moteur a découvert toutes les fonctions (alias "levage") avant d'en exécuter aucune. JS traite aussi les expressions comme paresseuses, donc ensemble, cela explique pourquoi la récurrence mutuelle fonctionne bien. Mais si JS n'avait pas de levage et/ou n'était pas paresseux, on peut imaginer que le moteur JS serait incapable de gérer la récurrence mutuelle car la référence circulaire entre a() et b() signifierait en fait que l'un des deux a toujours été déclaré "trop ​​tard".

C'est vraiment tout ce que je voulais dire dans le livre.

+0

Cela éclaircit ma question. Merci pour la réponse, et le cours, qui est fantastique. – rswerve

+0

Pourriez-vous élaborer sur "_JS traite également les expressions comme lazy_". JS n'est-il pas impatient d'être évalué? – rand

+0

Ce que je voulais dire, c'est que la valeur de 'a' dans' a() 'n'est pas résolue jusqu'au moment où elle est exécutée. –

-1

Le deuxième bloc de code fonctionne correctement car vous appelez a(1) après l'initialisation de toutes les fonctions. Essayez le bloc suivant:

var a = function(foo){ 
 
    if (foo > 20) return foo 
 
    return b(foo+2) 
 
} 
 

 
var b = function(foo){ 
 
    return c(foo) + 1 
 
} 
 

 
a(1); 
 

 
var c = function(foo){ 
 
    return a(foo*2) 
 
}

Cela donnera une erreur Uncaught TypeError: c is not a function parce que function assigned to c ne soit pas levée. C'est la raison pour laquelle vous avez besoin de levage. Parce que si vous déclarez des fonctions comme dans votre premier bloc de code, toutes les fonctions seront hissées et vous pouvez appeler a n'importe où dans le code. Ce n'est pas vrai dans les autres cas.

4

Commodité et placement de l'invocation mis à part, il n'y a pas de situations nécessitant un levage.

Assurez-vous de déclarer toutes les fonctions avant de les utiliser.

Remarque: Dans certains navigateurs, function a(){} crée une fonction avec le nom a alors que var a = function(){} ne l'est pas (fonction considérée comme anonyme). Le nom de la fonction est utilisé lors du débogage. Vous pouvez également faire var b = function a(){}.