Movatterモバイル変換


[0]ホーム

URL:


PCjs Machines

Home of the original IBM PC emulator for browsers.

Logo

PCjs Blog

PCjs Coding Conventions

Here are a few highlights of the (evolving) JavaScript coding conventions used in PCjs.

Tabs vs. Spaces

I’ve configured my IDE (WebStorm) to NEVER use tab characters in .js files(spaces only) and to ALWAYS use tab characters in almost every other type of text file. This is largely becausewhen a web browser displays a JavaScript file (either in the main window or in the Developer Tools window), tabsusually screw up the formatting, which I find annoying when I’m debugging. XML files, on the other hand,are usually reformatted by the browser anyway, so in those cases, I opt for smaller files and use real tabs.

Note that most of the JavaScript delivered by a PCjs production server will have been compiled by Google’sClosure Compiler, which completely eliminates all non-essential whitespace, so this is just a developmentpreference, with little to no impact on production files.

Regardless of the choice of tab character however, I almost always use 4-column tab stops, except in legacy .asmfiles, where 8-column tab stops were the norm.

I’ve noticed that 2-column tab stops have recently become popular, especially in Node projects; NPM, for example,will rewrite package.json files, replacing my 4-column spacing with 2-column spacing. I don’t fight that trend – Ijust ignore it.

Constants

Property names with all UPPER-CASE letters (with optional numbers and/or underscores) represent constants.

I originally adopted this rule in part because it’s a popular C language convention, but also because itmade it easy to write a preprocessing script (see the PCjs Grunt taskprepjs in /modules/grunts/prepjs/)that replaced all such property references with the corresponding property values and then removed the originalproperty definitions. Of course, this convention also depended on the properties never being modifiedor enumerated.

I later discovered that Google’s Closure Compiler does an excellent job of automatically inlining propertiesthat are never modified or enumerated, so theprepjs preprocessing script is no longer used, but I’ve stuckwith the UPPER-CASE convention.

I don’t bother with JSDoc@const annotations, because 1) the project contains far too many constants, 2)all the constants are already effectively annotated by virtue of being UPPER-CASE, and 3) there is no noticeableimprovement in the Closure Compiler’s inlining capability with the addition of@const.

All constants associated with a component are normally attached to the component’s constructor; ie, as properties ofthe constructor. If you think of a JavaScript constructor as a “class’, then constants attached to the constructorcan be thought of as “class constants”.

For example, the ChipSet component, which manages (among other things) Programmable Interrupt Controllers or PICs,could define the constant for an EOI command like this:

ChipSet.EOI=0x20;// non-specific EOI (end-of-interrupt)

but since the EOI command is actually one of a number Operation Command Words (specifically, OCW2), I include an“OCW2_” prefix in the constant name:

ChipSet.OCW2_EOI=0x20;// non-specific EOI (end-of-interrupt)

and since I also like to group constants that are associated with a particular register or port, and since I don’twant the ChipSet constructor becoming littered with property constants, I first define a constant object; in thiscase,PIC_LO:

ChipSet.PIC_LO={};ChipSet.PIC_LO.OCW2_EOI=0x20;// non-specific EOI (end-of-interrupt)ChipSet.PIC_LO.OCW2_EOI_SPEC=0x60;// specific EOIChipSet.PIC_LO.OCW2_EOI_ROT=0xA0;// rotate on non-specific EOIChipSet.PIC_LO.OCW2_EOI_ROTSPEC=0xE0;// rotate on specific EOI

By using fully-qualified property names for each constant, the code has a more C-like appearance (think#define)that’s also easier to preprocess.

However, I’ve gradually switched to the more conventional JavaScript object notation for class constants:

ChipSet.PIC_LO={OCW2_EOI:0x20,// non-specific EOI (end-of-interrupt)OCW2_EOI_SPEC:0x60,// specific EOIOCW2_EOI_ROT:0xA0,// rotate on non-specific EOIOCW2_EOI_ROTSPEC:0xE0// rotate on specific EOI};

because, again, the Closure Compiler does an excellent job inlining such constants (or indeed any property that isnever modifiedor enumerated).

DEBUG vs. RELEASE

While we’re talking about constants, it’s important to be aware of constants that are not scoped toany particular component.

In/machines/modules/v2/defines.js,DEBUG is set toTRUE,enabling all debug-only code by default. It is also declared as a@define so that the Closure Compiler canoverride it, setting it toFALSE and disabling debug-only code.

To ensure that debug-only code is not simplydisabled but alsoremoved, the code should be wrapped with:

if(DEBUG){[codetoberemovedbytheClosureCompiler]}

In many cases, the compiler is able to completely remove calls to debug-only class methods; eg:

Component.assert(off>=0&&off<this.cb);

However, calls to debug-only instance methods seem to be more problematic, so all such calls are wrapped; eg:

if(DEBUG)this.log('load("'+sFileURL+'")');

There are a number of other important shared constants in/machines/modules/v2/defines.jsand PCjs-specific constants in/machines/pcx86/modules/v2/defines.js; referto those files for more information.

Braces and Parentheses

Most opening braces appear at the end of the line containing the associated “if”, “while”, “for”, “switch”,“function”, etc, preceded by a single space. And most opening parentheses are also preceded by a single space,except when following “function” or a function name, in which case there is NO space.

There’s always the occasional exception. For example, the opening brace of all the top-level (documented)functions in a module may appear on its own line, because the extra whitespace can make the code a bit morereadable.

It’s also important to be aware of JavaScript’s automatic semicolon insertion feature and the associated danger ofputting an opening brace below areturn statement that wants to return an object literal. As long as you (andyour IDE) are aware of that specific danger, there’s no need to be dogmatic about opening braces.

Variable Names

I still tend to follow Charles Simonyi’s “Hungarian” namingconventions – or rather, a naming convention loosely inspired by Hungarian.

For example, if I need a string or numeric variable representing a “thing,” I will name it “sThing” if it’s astring or “iThing” if it’s a number (or possibly “nThings” if it represents a total of Things or “cThings”if it’s a counter of Things). If a string or numeric variable has a very short-term use, I’ll probably just nameit “s” or “i”.

As I mentionbelow, I still tend to distinguish single characters from strings too,which means I may sometimes prefix character variables with “ch” and character counters with “cch”.

Of course, variable name prefixes like “s” and “n” are irrelevant if you’ve already given your variables meaningfulnames like “nameOfPerson” or “numberOfPeople”. And that’s fine – I sometimes do that as well. But in general,I still prefer variable names like “sPerson” and “nPeople”.

I don’t try to come up with special prefixes for Objects. If there’s a Person object, for example, I’ll probablyuse colloquial names like “personHere” or “personThere”. I am stricter with Arrays though: I prefix array variableswith “a”, arrays of strings and numbers with “as” and “ai” (or “an”), arrays of arrays with “aa”, etc. As for Arraysof anything else, I usually don’t bother with anything more than an “a” prefix.

Quotation Marks

Because of my C background, I prefer to use double-quotes around multi-character strings and single quotesaround single-character strings. While the reasons for doing so are largely historical and currently irrelevant,characters are STILL the building blocks of strings, and even the JavaScript String class contains methods thatdeal with individual characters (eg, charCodeAt() and fromCharCode()). So for any code that deals explicitly withindividual characters, I like to reinforce that with single quotes.

Also, to emphasize that object property names aren’t really strings (even though strings can be used as propertynames), I tend to use single quotes when quoting property names. That does make me somewhat inconsistent withthe JSON standard, which insists that property names be double-quoted, but JSON.stringify() takes care of that, soit’s not really a problem. Besides, I have a lot of quibbles with the JSON standard, like its “disapproval” ofcomments and hexadecimal constants, and its failure to faithfully serialize and deserialize uninitialized Arrayobjects, but I’ll leave my gripes about JSON for another post.

Generally speaking, the only time I quote property names is when I have to. I’ll use the “dot” syntax; eg:

obj.prop=true;

instead of:

obj['prop']=true;

unless the property name doesn’t conform to variable name syntax (eg, if it starts with a digit) or if it’s a“public” property and therefore I can’t risk Google’s Closure Compiler “minifying” the property name to somethingelse.

I break my own quoting rules slightly when dealing with strings thatcontain double-quotes, since it’s more readableto put double-quotes inside single-quoted strings than to “escape” every double-quote with a backslash.

For code that I originally wrote in PHP and later ported to JavaScript, there was a tendency in the originalcode to always use double-quotes around strings and “escape” double-quotes regardless, and that tendency may lingerin code I didn’t feel like rewriting much, but the tendency was due more to idiosyncrasies of PHP than any conventionof mine; for example:

Because of PHP’s restrictions on single-quoted strings, I tended to avoid them. However, in JavaScript, thoserestrictions/features don’t exist.

JSDoc

Most of the PCjs code is documented withJSDoc annotations – notbecause I want to be able to generate documentation (although that’s something to think about), but becauseit’s the only way to tell both the Closure Compiler and my IDE exactly what data types are passed around.The goals are to minimize the number of “code inspection” warnings in the IDE and produce warning-freecompilations.

In order to use the Closure Compiler’s ADVANCED_OPTIMIZATIONS option and get maximum performance (and maximum“minification”, a form of “uglification”), every function and its parameters needs to be fully typed; otherwise,the Compiler generates way too many warnings/errors – at least, that was the case when I first started usingit a couple of years ago.

I’ve adopted a zero-tolerance policy for warnings: nothing gets checked in if the Closure Compiler generates evena single warning.

And finally, speaking of warnings, I’ve had to tellWebStorm to “shut up”about a few:

I acknowledge those those features can introduce bugs if you’re not careful, so I make sure I’m careful. I don’tsubscribe to the dogmatic approach that others (eg, the author of JSLint) take about so-called “risky” features.I agree that it’s always a good idea to walk to the crosswalk before crossing a street, but I don’t agree that it’snever a good idea to cross in the middle sometimes, too.

I’ve also made the following “weak warnings” instead of “warnings”:

because it’s a useful warning, but I don’t like being penalized for functions that have been “prototyped” a specificway but can’t always be implemented exactly as prototyped.

[GitHub Source]

Jeff Parsons
Sep 30, 2014


[8]ページ先頭

©2009-2025 Movatter.jp