46

I'm used to working with Java in which (as we know) each object is defined in its own file (generally speaking). I like this. I think it makes code easier to work with and manage.

I'm beginning to work with javascript and I'm finding myself wanting to use separate files for different scripts I'm using on a single page. I'm currently limiting myself to only a couple .js files because I'm afraid that if I use more than this I will be inconvenienced in the future by something I'm currently failing to foresee. Perhaps circular references?

In short, is it bad practice to break my scripts up into multiple files?

Peter Berg
  • 6,006
  • 8
  • 37
  • 51
  • 20
    Because of download times, you should always try to make your scripts a single, big, file. HOWEVER, if you use a minifier (which you *should*), they can combine multiple source files into one for you. So you can keep working on multiple files then minify them into a single file for distribution. – Dave Mar 13 '13 at 22:54
  • 2
    @Dave, you should definitely post that as an answer! – alestanis Mar 13 '13 at 22:54
  • @Dave: I'd upvote that if it were an answer. :D – Herbert Mar 13 '13 at 22:55
  • One class (module) per file is a good idea no matter what language you're in, if you care about OO. All you have to do is minify all your files into one – Ruan Mendes Mar 13 '13 at 22:55
  • Not necessarily - your browser is limited to number of concurrent files it can download at a time, and having one connection can be much more efficient. But you don't need three lines of JS in fifty different files either. I would suggest looking into Application Module Design and Require.js. – Jared Farrish Mar 13 '13 at 22:55
  • heh, ok, I posted it as an answer! – Dave Mar 13 '13 at 22:55
  • @Dave - That's dogma passing as fact. It's not that simple. – Jared Farrish Mar 13 '13 at 22:56
  • @JaredFarrish: can you explain that? I know keeping things like libraries separate (and using a public CDN) is good for caching, but is there any other case where splitting is beneficial? – Dave Mar 13 '13 at 22:58
  • 6
    @Dave Because the browser can download files in parallel (still execute sequentially), five 1MB files will download faster than a single 5MB file. See http://stackoverflow.com/questions/4877321/multiple-javascript-files-combine-into-one/4878038#4878038 – Ruan Mendes Mar 13 '13 at 23:02
  • @JaredFarrish: OK, see my comment on my answer. I'll update the answer with a discussion on all this. – Dave Mar 13 '13 at 23:03
  • @Dave - It entirely depends on the situation and what the project requires; not every script needs some massive asset merging cache CDN workflow to get a script to their end-users. There are situations where it's warranted, there are others where it's not as useful per the cost and complexity or whatnot. [Addy Osmani, as usual, does a good job parsing the differences](http://addyosmani.com/blog/large-scale-jquery/). – Jared Farrish Mar 13 '13 at 23:04

5 Answers5

29

There are lots of correct answers, here, depending on the size of your application and whom you're delivering it to (by whom, I mean intended devices, et cetera), and how much work you can do server-side to ensure that you're targeting the correct devices (this is still a long way from 100% viable for most non-enterprise mortals).

When building your application, "classes" can reside in their own files, happily.
When splitting an application across files, or when dealing with classes with constructors that assume too much (like instantiating other classes), circular-references or dead-end references ARE a large concern.
There are multiple patterns to deal with this, but the best one, of course is to make your app with DI/IoC in mind, so that circular-references don't happen.
You can also look into require.js or other dependency-loaders. How intricate you need to get is a function of how large your application is, and how private you would like everything to be.

When serving your application, the baseline for serving JS is to concatenate all of the scripts you need (in the correct order, if you're going to instantiate stuff which assumes other stuff exists), and serve them as one file at the bottom of the page.

But that's baseline.
Other methods might include "lazy/deferred" loading.
Load all of the stuff that you need to get the page working up-front.
Meanwhile, if you have applets or widgets which don't need 100% of their functionality on page-load, and in fact, they require user-interaction, or require a time-delay before doing anything, then make loading the scripts for those widgets a deferred event. Load a script for a tabbed widget at the point where the user hits mousedown on the tab. Now you've only loaded the scripts that you need, and only when needed, and nobody will really notice the tiny lag in downloading.

Compare this to people trying to stuff 40,000 line applications in one file.
Only one HTTP request, and only one download, but the parsing/compiling time now becomes a noticeable fraction of a second.

Of course, lazy-loading is not an excuse for leaving every class in its own file.
At that point, you should be packing them together into modules, and serving the file which will run that whole widget/applet/whatever (unless there are other logical places, where functionality isn't needed until later, and it's hidden behind further interactions).

You could also put the loading of these modules on a timer.
Load the baseline application stuff up-front (again at the bottom of the page, in one file), and then set a timeout for a half-second or so, and load other JS files.
You're now not getting in the way of the page's operation, or of the user's ability to move around. This, of course is the most important part.

Norguard
  • 26,167
  • 5
  • 41
  • 49
  • That's what I'm talking about. Didn't you write a long answer yesterday about deferred/promise objects for a `C++` guy? – Jared Farrish Mar 13 '13 at 23:14
  • 2
    I did. Long-answers are my thing, when it comes to questions that don't have simple answers. – Norguard Mar 13 '13 at 23:15
  • It was a good write-up. I also [wax pedantic in double-time at times](http://stackoverflow.com/questions/15314090/placeholder-text/15317084#15317084). Interesting stuff. – Jared Farrish Mar 13 '13 at 23:16
  • Well, shucks. Thanks for the vote of confidence, despite the fact that my sentences start to melt together in linear correlation to the start of the next work-day, and my willingness to reproof my writing is inversely proportionate to the time spent and lines written. Also, that is one particularly-pedantic piece on pseudo-element placeholder placement. – Norguard Mar 13 '13 at 23:28
  • JavaScript does not have classes: http://stackoverflow.com/questions/2752868/does-javascript-have-classes – stepanian Jan 01 '14 at 11:23
  • @stepanian Hence the quotes around "class". There are *lots* of ways to separate object-oriented code in Javascript, and a lot of opportunity for developing build-systems to put that code together. Everything from DI-systems to monolithic registries, et cetera. "Classes", "Components", "Modules", "Chunks of self-contained, atomic reusability" are all viable names. You can also use closure to emulate pretty much every aspect of a traditional class-based OO language, minus having multiple parents (which most disallow, anyway) but offer mixins, instead (better than interfaces/abstracts). – Norguard Jan 03 '14 at 17:38
  • @stepanian classes are also on the way. As are module-imports, html templates, html closures/encapsulation (Shadow DOM), native Promise support, generators, better list comprehensions... ...they aren't all immediately usable, and I wouldn't be likely to touch JS classes, given the benefits of data-driven, service/entity-based design, and how much ORM and rigid, strongly-typied classes don't help with that paradigm... ...but they'll be there, soon. – Norguard Jan 03 '14 at 17:43
  • @Norguard Using the term "class" in JavaScript creates unnecessary confusion for everyone. No one is debating with you over the capabilities of JavaScript as an object-oriented language. It is simply not a class-based language and has not classes. I am pretty sure that was by design and not necessarily a shortcoming. – stepanian Jan 03 '14 at 23:03
  • @stepanian I'd suggest reading section 14.5 of the ECMA-262 rev.6 draft. ECMAScript 6 will indeed have classes. As in `class MyClass extends MyParent { }` classes. My feelings about that aside, I use classes in the example for the semantic purpose of allowing people to understand the concept of "disparate pieces of modular reusability". JS gives you a million ways of doing everything. If you wrap everything in IIFEs, kept in separate files, how do you wire them together at runtime? What is the 1, singular, standard way? There isn't. People comfortable with C++/Java/PHP/Python/Ruby/etc get it. – Norguard Jan 04 '14 at 21:26
  • @stepanian Also, in my experience with people who work predominantly in Java or C#, those programmers who then find themselves thrown into a JS project will immediately gravitate toward the understanding that `function MyClass (a) { this.member = a; this.method = function () {}; } var myClass = new MyClass();` is the closest thing which JS provides to classes, and is thus the basis of their application structure, if they have little other JS experience. People seem to find comfort in `this` and `new`. Moreover, the answer is tailored to a question, which if you reread... – Norguard Jan 04 '14 at 21:40
27

Update from 2020: this answer is very old by internet standards and is far from the full picture today, but still sees occasional votes so I feel the need to provide some hints on what has changed since it was posted. Good support for async script loading, HTTP/2's server push capabilities, and general browser optimisations to the loading process over the years, have all had an impact on how breaking up Javascript into multiple files affects loading performance.

For those just starting out with Javascript, my advice remains the same (use a bundler / minifier and trust it to do the right thing by default), but for anybody finding this question who has more experience, I'd invite them to investigate the new capabilities brought with async loading and server push.

Original answer from 2013-ish:


Because of download times, you should always try to make your scripts a single, big, file. HOWEVER, if you use a minifier (which you should), they can combine multiple source files into one for you. So you can keep working on multiple files then minify them into a single file for distribution.

The main exception to this is public libraries such as jQuery, which you should always load from public CDNs (more likely the user has already loaded them, so doesn't need to load them again). If you do use a public CDN, always have a fallback for loading from your own server if that fails.

As noted in the comments, the true story is a little more complex;

Scripts can be loaded synchronously (<script src="blah"></script>) or asynchronously (s=document.createElement('script');s.async=true;...). Synchronous scripts block loading other resources until they have loaded. So for example:

<script src="a.js"></script>
<script src="b.js"></script>

will request a.js, wait for it to load, then load b.js. In this case, it's clearly better to combine a.js with b.js and have them load in one fell swoop.

Similarly, if a.js has code to load b.js, you will have the same situation no matter whether they're asynchronous or not.

But if you load them both at once and asynchronously, and depending on the state of the client's connection to the server, and a whole bunch of considerations which can only be truly determined by profiling, it can be faster.

(function(d){
    var s=d.getElementsByTagName('script')[0],f=d.createElement('script');
    f.type='text/javascript';
    f.async=true;
    f.src='a.js';
    s.parentNode.insertBefore(f,s);
    f=d.createElement('script');
    f.type='text/javascript';
    f.async=true;
    f.src='b.js';
    s.parentNode.insertBefore(f,s);
})(document)

It's much more complicated, but will load both a.js and b.js without blocking each other or anything else. Eventually the async attribute will be supported properly, and you'll be able to do this as easily as loading synchronously. Eventually.

Dave
  • 44,275
  • 12
  • 65
  • 105
  • Is it strictly download times? I was of the understanding that the concern is over the number of simultaneous http requests that a browser will handle - slightly different (maybe I'm being pedantic?) - but I have heard it said that there aren't the same restrictions over scripts as there are regular content/assets within a page thus mitigating any real issue, although I can't find any references. – Emissary Mar 13 '13 at 22:58
  • There are means to [combine assets](http://symfony.com/doc/2.0/cookbook/assetic/asset_management.html#combining-assets), but if we're talking about performance, leave it in the `HTML` file. That's the fastest per se, right? I doubt that works for every situation, though. – Jared Farrish Mar 13 '13 at 22:59
  • 1
    @JaredFarrish Javascript shouldn't be in the HTML because it's usually shared between pages on the same site, and you only want users to download it once. Also Emissary: both come into play. It does have longer download times simply because it needs to do more round-trips, but simultaneous connections can also be a concern. Unless you load scripts asynchronously, the browser will block loading anything (including other scripts) until the download has finished, so in the general case they're loaded one after another. – Dave Mar 13 '13 at 23:02
  • Rules are meant to be broken. Be a bit less inflexible in your imagining of what I'm talking about and try to see that it's not a rule, it's a best practice and a valid consideration that all should ponder and *decide* what to do. – Jared Farrish Mar 13 '13 at 23:12
  • @JaredFarrish maybe my updated answer fits your views better. I've tried to cover all the situations. – Dave Mar 13 '13 at 23:16
  • On the subject, to me the art of any profession is to spend maximal time solving *interesting* problems. Here is an engineering/system design problem, and except who is concerned, it's *not that interesting*. Yet these ideas spread like a virus. Me personally, I've had plenty of rigid ways of thinking about coding going back 10+ years. This is an interesting trade we practice, and without these types of "rules", we with thick skulls might never get to the point of understanding the reason for the rule, and that the reason is almost always more important that the rule itself. That is, *think*. – Jared Farrish Mar 13 '13 at 23:41
  • Unfortunatley, you are completely wrong. Sync JS does not stop loading other resources anymore. This was an information from 2006. In 2008 all browser vendors switched from blocking to preloading. (Microsoft was first!). Also all browsers switched form 2 http requests to 6 at once. As a result roundtrip time is overestimated by most developers. If there are 3 download slots in connecting/waiting state (round trip) the other 3 download slots can mostly fully utilize the connection capacity. Noadays it's sometimes better to split large and important files. Rules are meant to be broken!!! – alexander farkas Jan 03 '14 at 21:39
  • @alexanderfarkas interesting (and confirmed by some quick testing with Chrome's network tools). Clearly my information is outdated. Though having said that, I still seem to get better performance with my particular (moderately small) scripts once they are combined rather than sourced individually (my testing wasn't exactly scientific, though!). So it's another answer of: benchmark (as always) – Dave Jan 03 '14 at 23:07
  • @dave This really depends on the size of your scripts. It's overfill to have 5-8 or more scripts. So you have to always bundle, but mostly not into one file but 2-3 depending on the size of each file. I often use jQuery file size as the size threshold. If a file gets bigger than I start to split, if it's smaller I concat. I think for blocking content (styles + sync js in head), It is more important because you can utilize all 6 slots for blocking content and reduce white page time. It wouldn't be so good, if browser already starts downloading content images while the user can't see anything... – alexander farkas Jan 04 '14 at 11:57
4

There are two concerns here: a) ease of development b) client-side performance while downloading JS assets

As far as development is concerned, modularity is never a bad thing; there are also Javascript autoloading frameworks (like requireJS and AMD) you can use to help you manage your modules and their dependencies.

However, to address the second point, it is better to combine all your Javascript into a single file and minify it so that the client doesn't spend too much time downloading all your resources. There are tools (requireJS) that let you do this as well (i.e., combine all your dependencies into a single file).

Vivin Paliath
  • 94,126
  • 40
  • 223
  • 295
0

It's depending on the protocol you are using now. If you are using http2, I suggest you to split the js file. If you use http, I advise you to use minified js file.

Here is the sample of website using http and http2

Thanks, hope it helps.

roman
  • 788
  • 1
  • 10
  • 23
  • “While this link may answer the question, it is better to include the essential parts of the answer here and provide the link for reference. Link-only answers can become invalid if the linked page changes. – Divyang Desai Nov 25 '16 at 09:39
-4

It does not really matter. If you use the same JavaScript in multiple files, it can surely be good to have a file with the JavaScript to fetch from. So you just need to update the script from one place.

Treps
  • 780
  • 3
  • 12
  • 28
  • Are you suggesting one JS file for all your JavaScript? It does matter. – Ruan Mendes Mar 13 '13 at 23:07
  • If you look at the library for jQuery, it's a lot of code. Many people choose to use the whole package instead of breaking out the code needed. So I would say go with one file if you don't have a side with a lot of traffic. Otherwise, it's probably better to organize for better structure in the code. But mostly I thought about whether you want to use the same functions in different files. :) – Treps Mar 13 '13 at 23:21