forked from pandark/eloquent-javascript-translation
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathchapter9.html
217 lines (208 loc) · 30.9 KB
/
chapter9.html
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
<html>
<head>
<link rel="stylesheet" type="text/css" href="css/book.css"/>
<link rel="stylesheet" type="text/css" href="css/highlight.css"/>
<link rel="stylesheet" type="text/css" href="css/console.css"/>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
<title>Modularité -- JavaScript Éloquent</title>
</head>
<body>
<script type="text/javascript" src="js/before.js"> </script>
<div class="content">
<script type="text/javascript">var chapterTag = 'modularity';</script>
<div class="navigation">
<a href="chapter8.html"><< Chapitre précédent</a> |
<a href="contents.html">Table des matières</a> |
<a href="index.html">Couverture</a> |
<a href="chapter10.html">Chapitre suivant >></a>
</div>
<h1><span class="number">Chapitre 9 : </span>Modularité</h1>
<div class="block">
<p>Ce chapitre concerne les méthodes d'organisation des programmes. Pour les programmes de taille modeste, la question de l'organisation est rarement un problème. Mais quand un programme grandit, il peut atteindre une taille conséquente qui rend difficile à contrôler sa structure et son interprétation. Un tel programme commence assez facilement à ressembler à un plat de spaghetti, une masse informe dans laquelle tout semble relié à tout le reste.</p>
<p>Lorsque nous structurons un programme, nous faisons deux choses. Nous le divisons en plus petites parties appelées <a name="key1"></a>modules, chacune ayant un rôle spécifique, et nous spécifions les relations entre ces parties.</p>
<p>Dans le <a href="chapter8.html">chapitre 8</a>, en développant un terrarium, nous avons utilisé un grand nombre de fonctions décrites dans le <a href="chapter6.html">chapitre 6</a>. Ce dernier définissait également quelques nouveaux concepts qui n'avaient rien de spécifique aux terraria, comme les types <code>clone</code> et <code>Dictionary</code>. Toutes ces choses ont été ajoutées à l'environnement sans être organisées. Une façon de découper ce programme en modules pourrait être :</p>
<ul><li>
Pour commencer, un module <code>FunctionalTools</code> qui inclut les fonctions du <a href="chapter6.html">chapitre 6</a> et n'a pas de dépendance.
</li><li>
Ensuite, <code>ObjectTools</code>, contenant des choses comme <code>clone</code> et <code>create</code>, qui dépend de <code>FunctionalTools</code>.
</li><li>
<code>Dictionary</code> qui contient le type dictionary et dépend de <code>FunctionalTools</code>.
</li><li>
Enfin, le module <code>Terrarium</code> qui dépend de <code>ObjectTools</code> et <code>Dictionary</code>.
</li></ul>
<p>Quand un module <a name="key2"></a>dépend d'un autre module, il utilise des fonctions ou des variables de ce module, et fonctionnera seulement quand le module sera chargé.</p>
<p>C'est une bonne idée de s'assurer que les dépendances ne forment jamais une boucle. Non seulement les dépendances circulaires créent un problème technique (si les modules <code>A</code> et <code>B</code> dépendent l'un de l'autre, lequel doit être chargé en premier ?), mais elles rendent aussi les relations entre les modules moins évidentes et peuvent aboutir à une version modulaire des spaghettis dont je parlais plus tôt.</p>
</div><hr/><div class="block">
<p>La plupart des langages de programmation modernes ont un système de module intégré. Ce n'est pas le cas de JavaScript. Une fois encore, il nous faut inventer quelque chose nous-mêmes. Le plus évident pour commencer est de mettre chaque module dans un fichier différent. Cela permet de voir précisément à quel module appartient le code.</p>
<p>Les <a name="key3"></a>navigateurs chargent des fichiers JavaScript quand ils rencontrent une balise <code><script></code> avec un attribut <code>src</code>
dans le balisage HTML. L'extension <code>.js</code> est habituellement utilisée pour les fichiers contenant du code JavaScript. Dans la console, un raccourci pour charger les fichiers est fourni par la fonction <code>load</code>.</p>
<pre class="code"><span class="variable">load</span>(<span class="string">"FunctionalTools.js"</span>);</pre>
</div><hr/><div class="block">
<p>Dans certains cas, lancer des commandes dans le mauvais ordre provoquera des erreurs. Si un module essaie de créer un objet <code>Dictionary</code> alors que le module <code>Dictionary</code> n'a pas encore été chargé, il sera incapable de trouver le constructeur, et échouera.</p>
<p>On pourrait croire que ça se règle facilement. On ajoute simplement quelques appels à <code>load</code> en haut du fichier d'un module pour charger tous les modules dont il dépend. Malheureusement, compte-tenu du fonctionnement des navigateurs, appeler <code>load</code> ne provoque pas immédiatement le chargement d'un fichier donné. Le fichier sera chargé <em>après</em> l'exécution complète du fichier courant. C'est généralement trop tard.<p>
<p>Dans la plupart des cas, la solution pragmatique consiste à gérer les dépendances à la main : placez les balises <code>script</code> de vos documents HTML dans le bon ordre.</p>
</div><hr/><div class="block">
<p>Il existe deux moyens d'automatiser (partiellement) la gestion des dépendances. Le premier consiste à conserver dans un fichier distinct les informations concernant les dépendances entre les modules. Ce fichier peut être chargé en premier, et utilisé pour déterminer dans quel ordre charger les autres. Le seconde moyen consiste à ne pas utiliser de balise <code>script</code> (<code>load</code> crée et ajoute une telle balise par un mécanisme interne), mais à aller chercher le contenu du fichier directement (voir le <a href="chapter14.html">chapitre 14</a>), puis à utiliser la fonction <code>eval</code> afin de l'exécuter. Cela rend le chargement de scripts instantané, et ainsi plus facile à gérer.</p>
<p><a name="key4"></a><code>eval</code>, short for 'evaluate', is an interesting function. You give it a string value, and it will execute the content of the string as JavaScript code.</p>
<pre class="code"><span class="variable">eval</span>(<span class="string">"print(\"I am a string inside a string!\");"</span>);</pre>
<p>You can imagine that <code>eval</code> can be used to do some interesting things. Code can build new code, and run it. In most cases, however, problems that can be solved with creative uses of <code>eval</code> can also be solved with creative uses of anonymous functions, and the latter is less likely to cause strange problems.</p>
<p>When <code>eval</code> is called inside a function, all new variables will become local to that function. Thus, when a variation of the <code>load</code> would use <code>eval</code> internally, loading the <code>Dictionary</code> module would create a <code>Dictionary</code> constructor inside of the <code>load</code> function, which would be lost as soon as the function returned. There are ways to work around this, but they are rather clumsy.</p>
</div><hr/><div class="block">
<p>Let us quickly go over the first variant of dependency management. It requires a special file for dependency information, which could look something like this:</p>
<pre class="code"><span class="keyword">var</span> <span class="variable">dependencies</span> =
{<span class="string">"ObjectTools.js"</span>: [<span class="string">"FunctionalTools.js"</span>],
<span class="string">"Dictionary.js"</span>: [<span class="string">"ObjectTools.js"</span>],
<span class="string">"TestModule.js"</span>: [<span class="string">"FunctionalTools.js"</span>, <span class="string">"Dictionary.js"</span>]};</pre>
<p>The <code>dependencies</code> object contains a property for each file that depends on other files. The values of the properties are arrays of file names. Note that we could not use a <code>Dictionary</code> object here, because we can not be sure that the <code>Dictionary</code> module has been loaded yet. Because all the properties in this object will end in <code>".js"</code>, they are unlikely to interfere with hidden properties like <code>__proto__</code> or <code>hasOwnProperty</code>, and a regular object will work fine.</p>
<p>The dependency manager must do two things. Firstly it must make sure that files are loaded in the correct order, by loading a file's dependencies before the file itself. And secondly, it must make sure that no file is loaded twice. Loading the same file twice might cause problems, and is definitely a waste of time.</p>
<pre class="code"><span class="keyword">var</span> <span class="variable">loadedFiles</span> = {};
<span class="keyword">function</span> <span class="variable">require</span>(<span class="variabledef">file</span>) {
<span class="keyword">if</span> (<span class="variable">dependencies</span>[<span class="localvariable">file</span>]) {
<span class="keyword">var</span> <span class="variabledef">files</span> = <span class="variable">dependencies</span>[<span class="localvariable">file</span>];
<span class="keyword">for</span> (<span class="keyword">var</span> <span class="variabledef">i</span> = <span class="atom">0</span>; <span class="localvariable">i</span> < <span class="localvariable">files</span>.<span class="property">length</span>; <span class="localvariable">i</span>++)
<span class="variable">require</span>(<span class="localvariable">files</span>[<span class="localvariable">i</span>]);
}
<span class="keyword">if</span> (!<span class="variable">loadedFiles</span>[<span class="localvariable">file</span>]) {
<span class="variable">loadedFiles</span>[<span class="localvariable">file</span>] = <span class="atom">true</span>;
<span class="variable">load</span>(<span class="localvariable">file</span>);
}
}</pre>
<p>The <a name="key5"></a><code>require</code> function can now be used to load a file and all its dependencies. Note how it recursively calls itself to take care of dependencies (and possible dependencies of that dependency).</p>
<pre class="code"><span class="variable">require</span>(<span class="string">"TestModule.js"</span>);</pre>
<pre class="code"><span class="variable">test</span>();</pre>
</div><hr/><div class="block">
<p>Building a program as a set of nice, small modules often means the program will use a lot of different files. When programming for the web, having lots of small JavaScript files on a page tends to make the page slower to load. This does not have to be a problem though. You can write and test your program as a number of small files, and put them all into a single big file when 'publishing' the program to the web.</p>
</div><hr/><div class="block">
<p>Just like an object type, a module has an interface. In simple collection-of-functions modules such as <code>FunctionalTools</code>, the interface usually consists of all the functions that are defined in the module. In other cases, the interface of the module is only a small part of the functions defined inside it. For example, our manuscript-to-HTML system from <a href="chapter6.html">chapter 6</a> only needs an interface of a single function, <code>renderFile</code>. (The sub-system for building HTML would be a separate module.)</p>
<p>For modules which only define a single type of object, such as <code>Dictionary</code>, the object's interface is the same as the module's interface.</p>
</div><hr/><div class="block">
<p>In JavaScript, 'top-level' variables all live together in a single place. In browsers, this place is an object that can be found under the name <code>window</code>. The name is somewhat odd, <code>environment</code> or <code>top</code> would have made more sense, but since browsers associate a JavaScript environment with a window (or 'frame'), someone decided that <code>window</code> was a logical name.</p>
<pre class="code"><span class="variable">show</span>(<span class="variable">window</span>);
<span class="variable">show</span>(<span class="variable">window</span>.<span class="property">print</span> == <span class="variable">print</span>);
<span class="variable">show</span>(<span class="variable">window</span>.<span class="property">window</span>.<span class="property">window</span>.<span class="property">window</span>.<span class="property">window</span>);</pre>
<p>As the third line shows, the name <code>window</code> is merely a property of this environment object, pointing at itself.</p>
</div><hr/><div class="block">
<p>When much code is loaded into an environment, it will use many top-level variable names. Once there is more code than you can really keep track of, it becomes very easy to accidentally use a name that was already used for something else. This will break the code that used the original value. The proliferation of top-level variables is called <a name="key6"></a>name-space pollution, and it can be a rather severe problem in JavaScript ― the language will not warn you when you redefine an existing variable.</p>
<p>There is no way to get rid of this problem entirely, but it can be greatly reduced by taking care to cause as little pollution as possible. For one thing, modules should not use top-level variables for values that are not part of their external interface.</p>
</div><hr/><div class="block">
<p>Not being able to define any internal functions and variables at all in your modules is, of course, not very practical. Fortunately, there is a trick to get around this. We write all the code for the module inside a function, and then finally add the variables that are part of the module's interface to the <code>window</code> object. Because they were created in the same parent function, all the functions of the module can see each other, but code outside of the module can not.</p>
<pre class="code"><span class="keyword">function</span> <span class="variable">buildMonthNameModule</span>() {
<span class="keyword">var</span> <span class="variabledef">names</span> = [<span class="string">"January"</span>, <span class="string">"February"</span>, <span class="string">"March"</span>, <span class="string">"April"</span>,
<span class="string">"May"</span>, <span class="string">"June"</span>, <span class="string">"July"</span>, <span class="string">"August"</span>, <span class="string">"September"</span>,
<span class="string">"October"</span>, <span class="string">"November"</span>, <span class="string">"December"</span>];
<span class="keyword">function</span> <span class="variabledef">getMonthName</span>(<span class="variabledef">number</span>) {
<span class="keyword">return</span> <span class="localvariable">names</span>[<span class="localvariable">number</span>];
}
<span class="keyword">function</span> <span class="variabledef">getMonthNumber</span>(<span class="variabledef">name</span>) {
<span class="keyword">for</span> (<span class="keyword">var</span> <span class="variabledef">number</span> = <span class="atom">0</span>; <span class="localvariable">number</span> < <span class="localvariable">names</span>.<span class="property">length</span>; <span class="localvariable">number</span>++) {
<span class="keyword">if</span> (<span class="localvariable">names</span>[<span class="localvariable">number</span>] == <span class="localvariable">name</span>)
<span class="keyword">return</span> <span class="localvariable">number</span>;
}
}
<span class="variable">window</span>.<span class="property">getMonthName</span> = <span class="localvariable">getMonthName</span>;
<span class="variable">window</span>.<span class="property">getMonthNumber</span> = <span class="localvariable">getMonthNumber</span>;
}
<span class="variable">buildMonthNameModule</span>();
<span class="variable">show</span>(<span class="variable">getMonthName</span>(<span class="atom">11</span>));</pre>
<p>This builds a very simple module for translating between month names and their number (as used by <code>Date</code>, where January is <code>0</code>). But note that <code>buildMonthNameModule</code> is still a top-level variable that is not part of the module's interface. Also, we have to repeat the names of the interface functions three times. Ugh.</p>
</div><hr/><div class="block">
<p>The first problem can be solved by making the module function anonymous, and calling it directly. To do this, we have to add a pair of parentheses around the function value, or JavaScript will think it is a normal function definition, which can not be called directly.</p>
<p>The second problem can be solved with a helper function, <code>provide</code>, which can be given an object containing the values that must be exported into the <code>window</code> object.</p>
<pre class="code"><span class="keyword">function</span> <span class="variable">provide</span>(<span class="variabledef">values</span>) {
<span class="variable">forEachIn</span>(<span class="localvariable">values</span>, <span class="keyword">function</span>(<span class="variabledef">name</span>, <span class="variabledef">value</span>) {
<span class="variable">window</span>[<span class="localvariable">name</span>] = <span class="localvariable">value</span>;
});
}</pre>
<p>Using this, we can write a module like this:</p>
<pre class="code">(<span class="keyword">function</span>() {
<span class="keyword">var</span> <span class="variabledef">names</span> = [<span class="string">"Sunday"</span>, <span class="string">"Monday"</span>, <span class="string">"Tuesday"</span>, <span class="string">"Wednesday"</span>,
<span class="string">"Thursday"</span>, <span class="string">"Friday"</span>, <span class="string">"Saturday"</span>];
<span class="variable">provide</span>({
<span class="property">getDayName</span>: <span class="keyword">function</span>(<span class="variabledef">number</span>) {
<span class="keyword">return</span> <span class="localvariable">names</span>[<span class="localvariable">number</span>];
},
<span class="property">getDayNumber</span>: <span class="keyword">function</span>(<span class="variabledef">name</span>) {
<span class="keyword">for</span> (<span class="keyword">var</span> <span class="variabledef">number</span> = <span class="atom">0</span>; <span class="localvariable">number</span> < <span class="localvariable">names</span>.<span class="property">length</span>; <span class="localvariable">number</span>++) {
<span class="keyword">if</span> (<span class="localvariable">names</span>[<span class="localvariable">number</span>] == <span class="localvariable">name</span>)
<span class="keyword">return</span> <span class="localvariable">number</span>;
}
}
});
})();
<span class="variable">show</span>(<span class="variable">getDayNumber</span>(<span class="string">"Wednesday"</span>));</pre>
<p>I do not recommend writing modules like this right from the start. While you are still working on a piece of code, it is easier to just use the simple approach we have used so far, and put everything at top level. That way, you can inspect the module's internal values in your browser, and test them out. Once a module is more or less finished, it is not difficult to wrap it in a function.</p>
</div><hr/><div class="block">
<p>There are cases where a module will export so many variables that it is a bad idea to put them all into the top-level environment. In cases like this, you can do what the standard <code>Math</code> object does, and represent the module as a single object whose properties are the functions and values it exports. For example...</p>
<pre class="code"><span class="keyword">var</span> <span class="variable">HTML</span> = {
<span class="property">tag</span>: <span class="keyword">function</span>(<span class="variabledef">name</span>, <span class="variabledef">content</span>, <span class="variabledef">properties</span>) {
<span class="keyword">return</span> {<span class="property">name</span>: <span class="localvariable">name</span>, <span class="property">properties</span>: <span class="localvariable">properties</span>, <span class="property">content</span>: <span class="localvariable">content</span>};
},
<span class="property">link</span>: <span class="keyword">function</span>(<span class="variabledef">target</span>, <span class="variabledef">text</span>) {
<span class="keyword">return</span> <span class="variable">HTML</span>.<span class="property">tag</span>(<span class="string">"a"</span>, [<span class="localvariable">text</span>], {<span class="property">href</span>: <span class="localvariable">target</span>});
}
<span class="comment">/* ... many more HTML-producing functions ... */</span>
};</pre>
<p>When you need the content of such a module so often that it becomes cumbersome to constantly type <code>HTML</code>, you can always move it into the top-level environment using <code>provide</code>.</p>
<pre class="code"><span class="variable">provide</span>(<span class="variable">HTML</span>);
<span class="variable">show</span>(<span class="variable">link</span>(<span class="string">"http://docs.sun.com/source/816-6408-10/object.htm"</span>,
<span class="string">"This is how objects work."</span>));</pre>
<p>You can even combine the function and object approaches, by putting the internal variables of the module inside a function, and having this function return an object containing its external interface.</p>
</div><hr/><div class="block">
<p>When adding methods to standard prototypes, such as those of <code>Array</code> and <code>Object</code> a similar problem to name-space pollution occurs. If two modules decide to add a <code>map</code> method to <code>Array.prototype</code>, you might have a problem. If these two versions of <code>map</code> have the precise same effect, things will continue to work, but only by sheer luck.</p>
</div><hr/><div class="block">
<p>Designing an interface for a module or an object type is one of the subtler aspects of programming. On the one hand, you do not want to expose too many details. They will only get in the way when using the module. On the other hand, you do not want to be <em>too</em> simple and general, because that might make it impossible to use the module in complex or specialised situations.</p>
<p>Sometimes the solution is to provide two interfaces, a detailed 'low-level' one for complicated things, and a simple 'high-level' one for straightforward situations. The second one can usually be built very easily using the tools provided by the first one.</p>
<p>In other cases, you just have to find the right idea around which to base your interface. Compare this to the various approaches to inheritance we saw in <a href="chapter8.html">chapter 8</a>. By making prototypes the central concept, rather than constructors, we managed to make some things considerably more straightforward.</p>
<p>The best way to learn to the value of good interface design is, unfortunately, to use bad interfaces. Once you get fed up with them, you'll figure out a way to improve them, and learn a lot in the process. Try not to assume that a lousy interface is 'just the way it is'. Fix it, or wrap it in a new interface that is better (we will see an example of this in <a href="chapter12.html">chapter 12</a>).</p>
</div><hr/><div class="block">
<p>There are functions which require a lot of arguments. Sometimes this means they are just badly designed, and can easily be remedied by splitting them into a few more modest functions. But in other cases, there is no way around it. Typically, some of these arguments have a sensible 'default' value. We could, for example, write yet another extended version of <code>range</code>.</p>
<pre class="code"><span class="keyword">function</span> <span class="variable">range</span>(<span class="variabledef">start</span>, <span class="variabledef">end</span>, <span class="variabledef">stepSize</span>, <span class="variabledef">length</span>) {
<span class="keyword">if</span> (<span class="localvariable">stepSize</span> == <span class="atom">undefined</span>)
<span class="localvariable">stepSize</span> = <span class="atom">1</span>;
<span class="keyword">if</span> (<span class="localvariable">end</span> == <span class="atom">undefined</span>)
<span class="localvariable">end</span> = <span class="localvariable">start</span> + <span class="localvariable">stepSize</span> * (<span class="localvariable">length</span> - <span class="atom">1</span>);
<span class="keyword">var</span> <span class="variabledef">result</span> = [];
<span class="keyword">for</span> (; <span class="localvariable">start</span> <= <span class="localvariable">end</span>; <span class="localvariable">start</span> += <span class="localvariable">stepSize</span>)
<span class="localvariable">result</span>.<span class="property">push</span>(<span class="localvariable">start</span>);
<span class="keyword">return</span> <span class="localvariable">result</span>;
}
<span class="variable">show</span>(<span class="variable">range</span>(<span class="atom">0</span>, <span class="atom">undefined</span>, <span class="atom">4</span>, <span class="atom">5</span>));</pre>
<p>It can get hard to remember which argument goes where, not to mention the annoyance of having to pass <code>undefined</code> as a second argument when a <code>length</code> argument is used. We can make passing arguments to this function more comprehensive by wrapping them in an object.</p>
<pre class="code"><span class="keyword">function</span> <span class="variable">defaultTo</span>(<span class="variabledef">object</span>, <span class="variabledef">values</span>) {
<span class="variable">forEachIn</span>(<span class="localvariable">values</span>, <span class="keyword">function</span>(<span class="variabledef">name</span>, <span class="variabledef">value</span>) {
<span class="keyword">if</span> (!<span class="localvariable">object</span>.<span class="property">hasOwnProperty</span>(<span class="localvariable">name</span>))
<span class="localvariable">object</span>[<span class="localvariable">name</span>] = <span class="localvariable">value</span>;
});
}
<span class="keyword">function</span> <span class="variable">range</span>(<span class="variabledef">args</span>) {
<span class="variable">defaultTo</span>(<span class="localvariable">args</span>, {<span class="property">start</span>: <span class="atom">0</span>, <span class="property">stepSize</span>: <span class="atom">1</span>});
<span class="keyword">if</span> (<span class="localvariable">args</span>.<span class="property">end</span> == <span class="atom">undefined</span>)
<span class="localvariable">args</span>.<span class="property">end</span> = <span class="localvariable">args</span>.<span class="property">start</span> + <span class="localvariable">args</span>.<span class="property">stepSize</span> * (<span class="localvariable">args</span>.<span class="property">length</span> - <span class="atom">1</span>);
<span class="keyword">var</span> <span class="variabledef">result</span> = [];
<span class="keyword">for</span> (; <span class="localvariable">args</span>.<span class="property">start</span> <= <span class="localvariable">args</span>.<span class="property">end</span>; <span class="localvariable">args</span>.<span class="property">start</span> += <span class="localvariable">args</span>.<span class="property">stepSize</span>)
<span class="localvariable">result</span>.<span class="property">push</span>(<span class="localvariable">args</span>.<span class="property">start</span>);
<span class="keyword">return</span> <span class="localvariable">result</span>;
}
<span class="variable">show</span>(<span class="variable">range</span>({<span class="property">stepSize</span>: <span class="atom">4</span>, <span class="property">length</span>: <span class="atom">5</span>}));</pre>
<p>The <code>defaultTo</code> function is useful for adding default values to an object. It copies the properties of its second argument into its first argument, skipping those that already have a value.</p>
</div><hr/><div class="block">
<p>A module or group of modules that can be useful in more than one program is usually called a <a name="key7"></a>library. For many programming languages, there is a huge set of quality libraries available. This means programmers do not have to start from scratch all the time, which can make them a lot more productive. For JavaScript, unfortunately, the amount of available libraries is not very large.</p>
<p>But recently this seems to be improving. There are a number of good libraries with 'basic' tools, things like <code>map</code> and <code>clone</code>. Other languages tend to provide such obviously useful things as built-in standard features, but with JavaScript you'll have to either build a collection of them for yourself or use a library. Using a library is recommended: It is less work, and the code in a library has usually been tested more thoroughly than the things you wrote yourself.</p>
<p>Covering these basics, there are (among others) the 'lightweight' libraries <a href="http://www.prototypejs.org/">prototype</a>, <a href="http://mootools.net">mootools</a>, <a href="http://jquery.com">jQuery</a>, and <a href="http://mochikit.com">MochiKit</a>. There are also some larger 'frameworks' available, which do a lot more than just provide a set of basic tools. <a href="http://developer.yahoo.com/yui/">YUI</a> (by Yahoo), and <a href="http://dojotoolkit.org/">Dojo</a> seem to be the most popular ones in that genre. All of these can be downloaded and used free of charge. My personal favourite is MochiKit, but this is mostly a matter of taste. When you get serious about JavaScript programming, it is a good idea to quickly glance through the documentation of each of these, to get a general idea about the way they work and the things they provide.</p>
<p>The fact that a basic toolkit is almost indispensable for any non-trivial JavaScript programs, combined with the fact that there are so many different toolkits, causes a bit of a dilemma for library writers. You either have to make your library depend on one of the toolkits, or write the basic tools yourself and include them with the library. The first option makes the library hard to use for people who are using a different toolkit, and the second option adds a lot of non-essential code to the library. This dilemma might be one of the reasons why there are relatively few good, widely used JavaScript libraries. It is possible that, in the future, new versions of ECMAScript and changes in browsers will make toolkits less necessary, and thus (partially) solve this problem.</p>
</div>
<div class="navigation">
<a href="chapter8.html"><< Chapitre précédent</a> |
<a href="contents.html">Table des matières</a> |
<a href="index.html">Couverture</a> |
<a href="chapter10.html">Chapitre suivant >></a>
</div>
<div class="footer">
© <a href="mailto:marijnh@gmail.com">Marijn Haverbeke</a>
(<a href="http://creativecommons.org/licenses/by/3.0/deed.fr">licence</a>),
écrit entre mars et juillet 2007, dernière modification le 11 juillet 2011.
</div>
</div>
<script type="text/javascript" src="js/ejs.js"> </script>
</body>
</html>