General question: how to keep JavaScript organized

Started by
10 comments, last by bassy 9 years, 4 months ago

I have a question that is both game-related and useful for other web-based projects (though in the examples I below try to keep it focused on games). To put it simply, what are the best strategies for keeping JavaScript organized?

Let me clarify: in most of the languages I know (C#, Java, PHP, AutoHotkey, even my limited knowledge of C++) we have classes. And while I know not everyone is a big fan of object-oriented programming, I personally find it makes things a heck of a lot easier to understand and maintain. Each class is usually in a separate file, and once created it's easy to use them (i.e. Player.SwingSword(), Wizard.TeleportTo(x,y), etc.). You know which objects are doing what, and you can use the same class in different games. Even the HTML DOM seems to fall into this category (it is the document "object" model, after all).

But JavaScript is totally different. You can do it in an object-oriented way, but those objects come in the form of functions. It also has some great event handlers (even touch event handlers for mobile devices), but those also look like functions. And you can also just create functions independent of any kind of object or event. So with all these functions floating around, things seem to get confusing fast. Regardless of whether I'm using OOP or not, creating a game or not, my scripts always end up with a long list of functions that just keeps growing and growing.There's got to be a better way.

So what do you guys do to help keep your JavaScript in order? And please, don't even go there with things like JQuery, Node.js, Ajax and other libraries/APIs. I have no problem reading up on APIs in class-based languages, but before I can even touch that in JavaScript I've got to get a handle on managing my own code. Before yesterday (when I was assigned a JavaScript project at work) I didn't even like to use JavaScript at all for this same reason. I get that it can do amazing things with the web, and I'd like to learn to use it better, but at this point I'm just not there yet. If I can get this figured out, then I'll be able to move forward into the deeper stuff, but for now I'm just trying to get the hang of ordinary JavaScript.

Advertisement

Use a module system.

Javascript is already OOP, everything is an object (strings, arrays, numbers, functions). Javascript also has classes, with inheritance and all that jazz, the inheritance is based on prototypes however and is not so called 'classical' inheritance. It is, however, more or less the same thing. (ES6 is even bringing the reserved keyword class into use). You are completely wrong, JavaScript is not 'totally' different. If you want a language that is, go learn something like Lisp or Haskell to see what 'totally different' actually looks like.

There are plenty of module libraries to make things simple (requirejs, commonjs etc), or you can write modules on your own without a library wrapper. (research the module pattern).

OOP languages have never been very organized, this is a myth - there is absolutely nothing stopping you from writing sphagetti code in one (nested classes, static functions, function/method sphaghetti). They do, however, tend to have really nice embedded module systems (usually called packages).

I personally love the ability to stick all the code in one file. I just re-factor it out every so often, whichever language I'm using (if your language can't do that, its probably junk). Usually my max is 1k lines of code, for more compact languages its 100 lines.

The reason you hate JS for being unorganized is probably because you've only ever seen JS written by people who didn't know JavaScript (reading poorly written C++, Java, or PHP is pretty painful as well), or you've seen some compiled JS which is intentionally horrible to read. Libraries like jQuery that use module systems are pretty well organized.

Hi bassy,

You make some very interesting points. I've never heard of JavaScript "modules" but I looked them up. Turns out it can be used in ways that are a lot more similar to the other languages I know. In particular this link:

http://briancray.com/posts/javascript-module-pattern

This person set up a bit of JavaScript that may as well be a class, in a way that was much cleaner and more straightforward than my usual "function spaghetti" (btw that was awesome lol). I will definitely continue to research this. I'm not sure what a "function expression" is yet, and I never did really get the whole "prototype" thing either, but this is a huge step in the right direction.

By the way, fyi I don't *hate* JavaScript, I just don't fully understand it (which makes it harder than usual). You're right: technically, it's not "totally" different; but it's different enough that some of the concepts don't make sense to me, which makes it a pain to use. You were also right that all I've seen is lousy examples from tutorial sites and intentionally messed-up source code (no whitespace so less to load). In fact, speaking of which, do you know of some good sites to learn it?

But anyway, thanks for the pointer. I think this was the answer I was looking for.


This person set up a bit of JavaScript that may as well be a class

It is not 'may as well be' - you need to change the way you think. It IS a class, just packaged differently. Remember, a class is nothing more than some bundled properties with attached methods that you can clone around places. The syntax you are used to is technically termed 'syntactical sugar' - i.e. it is a particular way of saying something.

As for sites, stop using the internet to learn JS. Go find a book or a course - you'll learn more, faster. After you've done that, there are plenty of good references online, e.g. MDN.

Re the above comments, I've identified three distinct meanings for the word "class." The one used above is the OOP class, which is a software module (commonly a file) that contains constructor code, property definitions, method code and so on. This is called a "class" in OOP languages like C++ and its progeny (such as Java). Many languages follow this model. For examples, Basic, Fortran and Cobol in their more recent versions.

A "class" is also a set of objects that have many or all properties in common (think Professor Boring's Comp Sci class). In software this is usually the set of objects descended from a single constructor.

See my _JavaScript Inheritance and Object Programming_ if you really need the third definition. It's the sort of thing one puts in a book for completeness.

When you write JavaScript you can organize your object definitions in OOP-style classes, but these are ususally short enough so that one-per-file becomes a nuisance. I myself look for 500 to 1000 line files and group object definition code accordingly. I also am a big fan of the classic (Fortran, 1957!) library pattern. I usually put all my library funcs in a single file. In can grow quite large put interdependencies are minimal.

But let me discourage you from writing JavaScript in a class-oriented, OOP way. Almost all my work is done by objects, but many of them are created _ex nihilo_ (literall, "from nothing") as JS object literals. I also use "capabilities" that cross multiple objects. See the "mixin" pattern for the more common way of doing the same thing.

Google me (or Amazon me) if you want to discuss this further.

To clarify what MartinRinehart is trying to say when he makes this nonsensical sentence:


But let me discourage you from writing JavaScript in a class-oriented, OOP way. Almost all my work is done by objects

(nonsensical as it clearly contradicts itself)

Don't let idioms from previous languages rule your current language, i.e. you are better off not trying to Java-style create a class for everything that needs to run a 'capability' or more commonly known as a 'function'. The module pattern provides you with the flexibility to utilize 'mixin' and hierarchical inheritance on your prototype chains.

More specifically, there is no reason not to create a Person, and a Ninja, and a Baker, but there is no need for nonsense such as a StatsProvider and then ninja.setStatsProvider(someSubclassOfStatsProvider) when you can just ninja.getStats = function() {}

With all due respect, bassy, if you took the trouble to read my book you would understand that there is no contradiction. And 'capabilities', as I use them, are not just functions.

In JS you can (and I think most well-written JS frequently does) create an object with an object literal: var person = { name: 'Martin Rinehart', sex: 'M', ... };

This is an object not created from a class software module. You use an OOP-style class software module when you wish to create lots of Person objects (a class of objects, 2nd definition). Then it becomes var person = new Person('Martin Rinehart', 'M', ... );

A 'capability' is something than an object can do.

In my windowing system example, many on-screen objects share the Movable capability (ability to be dragged by their title bars). The Movable capability adds the title bar, adds listeners for mouse down/move/up, and so on. It includes quite a few functions and additional properties. Called by my 'implements' method you make something a Movable screen object by: screen_object.implements( Movable, "Title Bar String" );

As with all good object software, all the details are encapsulated. Unlike OOP there is no software module that looks like a C++ or Java 'class'.

In Java, for example, you would get this result by defining an interface (in a class module) and implementing the interface in another class module that extended some base on-screen object class. In C++ you could create two base classes modules, one for being on-screen (a DOM cover) and another for being Movable. A third class could use multiple inheritance to create the desired Movable screen object. But those designs are really workarounds for the weakness of the OOP model: you cannot modify objects at run time, except to modify their properties' values.

In JavaScript the Movable capability (or any mixin) simply attaches additional properties as needed at run time and gives those properties appropriate values (where 'property' may be either data or code). JavaScript is so much richer it its power to manipulate objects that one can do most of one's work with objects, but without OOP-like classes. I'm surprised you haven't read Crockford's _Good Parts_. He entirely rejects the JS 'new' operator and never uses OOP classes for his object work.

If you search for "right way to learn JavaScript", there's a lot of decent advice on how not to fall into the abundant traps that exist due to the immense about of bad JS that exists out there.

BTW, js's particular flavour of OO is the prototype paradigm -
http://en.m.wikipedia.org/wiki/Prototype-based_programming

Lastly, the kind of inheritance that C++/Java are renowned for shouldn't actually be very common in good C++ code. This over-use of inheritance is a remnant of the 90's when OOP was a new fad that everyone dived into without actually groking, resulting in lots of bad code, and then lots of people learning from that bad code... Much like JS :)
Writing C++ using composition over inheritance and lots of std::function instead of virtual would be a great eye opener if you're a C++ coder that wants to gain new perspectives on OOP.
Learning JS (properly) will be similarly eye opening.

In Java, for example, you would get this result by defining an interface ... In C++ you could create two base classes modules [and] multiple inheritance
...
But those designs are really workarounds for the weakness of the OOP model: you cannot modify objects at run time, except to modify their properties' values.

That's not an issue with OO itself. Firstly it's a side-effect of inheritance-addiction like I mentioned above, and secondly a problem caused by static typing.

The reason JS excels here is because it has duck typing, which lets you dynamically use properties without statically defining structures. From a C++/Java-esque background, this seems like "everything is virtual", but of course is so much more than that.


var aDuck = { quack = function(){alert('quack');}; };
var aPerson = { quack = function(){alert('Hello!');}; };
function quack( who ){ who.quack(); }
quack(aDuck);//quack
quack(aPerson);//Hello!


C++ only has duck typing inside of templates, e.g.


struct Duck { void Quack() { print("quack"); } };
struct Person { void Quack() { print("Hello!"); } };
template<class T> void Quack( T& object ) { object.Quack(); }
void Test() {
  Duck aDuck;
  Person aPerson;
  Quack(aDuck);//quack
  Quack(aPerson);//Hello!
}

And yes as you mention, at other times where static typing is enforced more strongly, you can instead use interfaces like this in C++ (Java is identical, except the syntax differs):


struct IQuackable { virtual void Quack() = 0; }
struct Duck : public virtual IQuackable { void Quack() { print("quack"); } };
struct Person : public virtual IQuackable { void Quack() { print("Hello!"); } };
void Quack( IQuackable& quackable ) { quackable.Quack(); }
void Test() {
  Duck aDuck;
  Person aPerson;
  Quack(aDuck);//quack
  Quack(aPerson);//Hello!
}

However... that's not the only solution that 'OO' offers. A core rule of OO is that you should default to using composition, and only use inheritance where necessary (Java shits all over this rule, so IMHO I wouldn't choose to recognize it a real OO language until it agrees to an intervention tongue.png).

JS has first-class functions, and C++ almost does (relying on library support over language support) -- which lets you rewrite the above interface/inheritance based solution as a function/composition based one, like this:


typedef std::function<void()> Quacker;
struct Duck { void Quack() { print("quack"); } Quacker GetQuacker() { return std::bind(&Quack, this, _1)); } };
struct Person { void Quack() { print("Hello!"); } Quacker GetQuacker() { return std::bind(&Quack, this, _1)); } };
void Quack( FnQuack& quacker ) { quacker(); }
void Test() {
  Duck aDuck;
  Person aPerson;
  Quack(aDuck.GetQuacker());//quack
  Quack(aPerson.GetQuacker());//Hello!
}

Again, after learning JS properly, trying bringing back all the wonderful new perspectives of it's paradigms back to other languages like C++ biggrin.png

C# also does pretty well in this regard. Anonymous functions, closures, delegates, events and generics are great tools to have in your toolbox. In C# 4 there's actually full blown duck typing available! Another way to practice JS-style thinking to to just try using these other tools instead of interfaces all the time in the languages you already know.

JavaScript is usually bound to HTML documents. Much like in C++ where you #include your headers, in HTML you <script> them. The browser reads the file top-down (again, much like C++).

It is a very bad idea to make the browser download a lot of files, so you usually bundle and minify your JavaScript.

It is very rare to split your code like in Java where every class gets its own file. When you write a bit more complex code, you usually wrap it inside anonymous function which you immediately execute to avoid naming conflicts. Assigning the result of the function is kind of.. weird. You usually strive to put code together, you would only do that if you wanted to expose something as public API - and this is rare in JavaScript (unless you were writing a library).

If you really wanted the module systems where JS files themselves include other JS, you would use RequireJS. I haven't looked into their code, but the idea is trivial: you dynamically append a <script> into your <head> and wait until the browser downloads and parses the file (much like you would add an <img>). RequireJS is relatively popular, has tools to optimize your JS (understand: minify and bundle), and you are likely to find answers to your questions at StackOverflow, and also has surprisingly good documentation.

You didn't say what your project is. Is it browser-based (then just include the <script>-s in the appropriate order), or is it embedded (node, chromium)? How you make script files reference each other depends on how JS is hosted. In any case, since everything in JS is an object, the so described "modules" are just functions where you expose public API by adding it to this (e.g. this.killGoblins = function() {..}), or, if you really needed classes, using the prototype syntax (var goblin = function() {}; goblin.prototype = { object literal };). In most cases, you don't really need classes, you use duck typing. In JS, you've got a lot of free functions just because you don't really need classes and objects most of the time.

How I organize my code depends very much on what I'm doing. Is it a web site? Then follow whatever the framework I'm using dictates. Is it a game? If I can do it in a single file, I would. If the code is bigger, then depend on the <script> order in my JS files (usually located in /js or similar). It may sound horrible to you, but I try to do the simples possible thing first. Plus, in JS, you rarely have to have explicit types.

If you want your code to be more organized and be closer to what you are used to from strongly-typed languages, take a look at TypeScript. I has modules, supports including (and then bundling) multiple files, etc.

This topic is closed to new replies.

Advertisement