J'ai lu récemment quelques articles (par exemple http://dailyjs.com/2012/09/14/functional-programming/ ) sur les aspects fonctionnels de Javascript et la relation entre Scheme et Javascript (ce dernier a été influencé par le premier, qui est un langage fonctionnel, tandis que les aspects OO sont hérités de Self qui est un langage basé sur le prototypage).
Cependant ma question est plus précise: je me demandais s'il y avait des métriques sur les performances de la récursion vs l'itération en Javascript.
Je sais que dans certaines langues (où, par conception, l'itération fonctionne mieux), la différence est minime car l'interpréteur/compilateur convertit la récursivité en itération, mais je suppose que ce n'est probablement pas le cas de Javascript car il s'agit, au moins partiellement, d'une fonction Langue.
JavaScript n'effectue pas d'optimisation de récursivité de queue , donc si votre récursivité est trop profonde, vous pouvez obtenir un débordement de pile d'appels. L'itération n'a pas de tels problèmes. Si vous pensez que vous allez trop récursif et que vous avez vraiment besoin d'une récursivité (par exemple, pour effectuer un remplissage par saturation), remplacez la récursivité par votre propre pile.
Les performances de récursivité sont probablement pires que les performances d'itération, car les appels et les retours de fonctions nécessitent une préservation et une restauration d'état, tandis que l'itération saute simplement à un autre point dans une fonction.
Mise à jour : depuis ES2015, JavaScript a TCO , donc une partie de l'argument ci-dessous ne tient plus.
Bien que Javascript n'ait pas d'optimisation des appels de queue, la récursivité est souvent la meilleure façon de procéder. Et sincèrement, sauf dans les cas Edge, vous n'obtiendrez pas de débordements de pile d'appels.
Les performances sont quelque chose à garder à l'esprit, mais aussi une optimisation prématurée. Si vous pensez que la récursivité est plus élégante que l'itération, alors allez-y. S'il s'avère que c'est votre goulot d'étranglement (qui peut ne jamais l'être), vous pouvez le remplacer par une itération laide. Mais la plupart du temps, le goulot d'étranglement réside dans les manipulations DOM ou plus généralement les E/S, pas le code lui-même.
La récursivité est toujours plus élégante1.
1: Opinion personnelle.
J'étais également assez curieux de connaître ces performances en javascript, j'ai donc fait quelques expériences (bien que sur une ancienne version de node). J'ai écrit une calculatrice factorielle récursivement par rapport aux itérations et l'ai exécutée plusieurs fois localement. Le résultat semblait assez fortement biaisé vers une récursivité ayant une taxe (attendue).
Code: https://github.com/j03m/trickyQuestions/blob/master/factorial.js
Result:
j03m-MacBook-Air:trickyQuestions j03m$ node factorial.js
Time:557
Time:126
j03m-MacBook-Air:trickyQuestions j03m$ node factorial.js
Time:519
Time:120
j03m-MacBook-Air:trickyQuestions j03m$ node factorial.js
Time:541
Time:123
j03m-MacBook-Air:trickyQuestions j03m$ node --version
v0.8.22
Selon demande de l'OP je vais participer (sans me ridiculiser, j'espère: P)
Je pense que nous sommes tous d'accord pour dire que la récursivité est juste une façon plus élégante de coder. Si cela est bien fait, cela peut rendre le code plus maintenable, ce qui est à mon humble avis tout aussi important (sinon plus) que le rasage de 0,0001 ms.
En ce qui concerne l'argument selon lequel JS n'effectue pas l'optimisation de l'appel de queue: ce n'est plus tout à fait vrai, en utilisant le mode strict d'ECMA5 active le TCO. C'était quelque chose dont je n'étais pas trop heureux il y a quelque temps, mais au moins je sais maintenant pourquoi arguments.callee
renvoie des erreurs en mode strict. Je connais le lien ci-dessus qui renvoie à un rapport de bogue, mais le bogue est réglé sur WONTFIX. Par ailleurs, le TCO standard arrive: ECMA6 (décembre 2013).
Instinctivement, et fidèle à la nature fonctionnelle de JS, je dirais que la récursivité est le style de codage le plus efficace 99,99% du temps. Cependant, Florian Margaine a raison de dire qu'il est probable que le goulot d'étranglement se trouve ailleurs. Si vous manipulez le DOM, vous devez probablement vous concentrer sur l'écriture de votre code aussi maintenable que possible. L'API DOM est ce qu'elle est: lente.
Je pense qu'il est presque impossible d'offrir une réponse définitive quant à l'option la plus rapide. Dernièrement, beaucoup de jspref que j'ai vus montrent que le moteur V8 de Chrome est ridiculement rapide pour certaines tâches, qui fonctionnent 4 fois plus lentement sur SpiderMonkey de FF et vice versa . Les moteurs JS modernes ont toutes sortes de trucs pour optimiser votre code. Je ne suis pas un expert, mais je pense que la V8, par exemple, est hautement optimisée pour les fermetures (et la récursivité), contrairement au moteur JScript de MS. SpiderMonkey fonctionne souvent mieux lorsque le DOM est impliqué ...
En bref: je dirais que la technique qui sera la plus performante est, comme toujours en JS, presque impossible à prévoir.
Sans mode strict, les performances d'itération sont généralement légèrement plus rapides que la récursivité ( en plus de faire travailler le JIT plus ). L'optimisation de la récursivité de queue élimine essentiellement toute différence notable car elle transforme la séquence d'appel entière en un saut.
Exemple: Jsperf
Je suggère de s'inquiéter beaucoup plus de la clarté et de la simplicité du code lorsqu'il s'agit de choisir entre la récursivité et l'itération.