Moved a few files and added a new post about JavaScript coding conventions

This commit is contained in:
Jeff Parsons 2014-09-30 10:57:34 -07:00 committed by jeffpar
commit 9cefe2e700
9 changed files with 179 additions and 16 deletions

154
blog/2014/09/30/README.md Normal file
View file

@ -0,0 +1,154 @@
My (JavaScript) Coding Conventions
---
Some ramblings about my JavaScript coding conventions. This is not an attempt to change anyone's mind
about anything, just an explanation of why things are they way they are (and will be more useful once PCjs
moves from a private to a public GitHub repository).
### Tabs vs. Spaces
I've configured my IDE ([PhpStorm](http://www.jetbrains.com/phpstorm/)) 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 because
when a web browser displays a JavaScript file (either in the main window or in the Developer Tools window), tabs
usually 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's
Closure Compiler, which completely eliminates all non-essential whitespace, so this is really a development
preference, 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 .asm
files, 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 -- I just ignore it.
### Braces and Parentheses
Most of my 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 of my opening parentheses are also preceded by a single space,
except when following "function" or a function name, in which case there is NO space. This is just an historical
preference, dating back to my BASIC and C programming days; it's not that those preferences matter anymore, it's just
that I see no reason to change them.
Sometimes I break these conventions though. For example, for all the top-level (documented) functions in a
module, I'll often move the opening brace of the function body to its own line, because I feel that the extra
whitespace makes the code a bit more readable. It may sometimes depend on my mood, but I do try to be consistent
within a given file at least.
### Variable Names
I still tend to follow Charles Simonyi's "[Hungarian](http://en.wikipedia.org/wiki/Hungarian_notation)" naming
conventions -- or rather, a naming convention loosely inspired by Hungarian. I know lots of people sneer at
those conventions and think they're useless, and all I can say is, they're wrong: they are not useless to ME.
I admit they may be useless to anyone who has a phenomenal memory and can remember that an obscure variable named
"foo" was initialized with a string or a number, or whose IDE can answer that question with the press of a key (or two),
but for me, with my non-phenomenal memory and lazy fingers, I prefer being able to simply look at a variable to
immediately know what *type* of data it contains, if nothing else.
I rarely name a string or numeric variable something vague like "foo." At worst, I would name it "sFoo" if it
was a string or "iFoo" if it was a number (or possibly "nFoo" or "cFoo" if it represented a total of Foos or a
counter of Foos). And if a string or numeric variable has a very short-term use, I'll probably just name it "s"
or "i" (or "n").
As I mention [below](./#quotation-marks), 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, these letter prefixes like "s" and "n" are irrelevant if you already give your variables meaningful
names like "nameOfPerson" or "numberOfPeople". And that's fine -- I do that sometimes, too. 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 probably
use colloquial names like "personHere" or "personThere". I am stricter with Arrays though: I prefix array variables
with "a", arrays of strings and numbers with "as" and "ai" (or "an"), arrays of arrays with "aa", etc. As for Arrays
of anything else, I usually don't bother with anything more than an "a" prefix.
### Quotation Marks
Coming from a long C background, I prefer to use double-quotes around multi-character strings and single quotes
around 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 that
deal with individual characters (eg, charCodeAt() and fromCharCode()). So for any code that deals explicitly with
individual 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 property
names), I tend to use single quotes when quoting property names. That does make me somewhat inconsistent with
the JSON standard, which insists that property names be double-quoted, but JSON.stringify() takes care of that, so
it's not really a problem. Besides, I have a lot of quibbles with the JSON standard, like its "disapproval" of
comments and hexadecimal constants, and its failure to faithfully serialize and deserialize uninitialized Array
objects, 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 something
else.
I break my own quoting rules slightly when dealing with strings that *contain* double-quotes, since it's more readable
to 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 original
code to always use double-quotes around strings and "escape" double-quotes regardless, and that tendency may linger
in code I didn't feel like rewriting much, but the tendency was due more to idiosyncrasies of PHP than any convention
of mine; for example:
- single-quoted PHP strings may not include any escaped characters (except for single-quote and backslash)
- single-quoted PHP strings cannot resolve references to string variables (eg, "the value of foo is {$foo}")
Because of PHP's restrictions on single-quoted strings, I tended to avoid them. However, in JavaScript, those
restrictions/features don't exist.
### JSDoc
I've taken a great deal of care to "[JSDoc](http://usejsdoc.org/)-ify" nearly all my JavaScript code, not
because I want to be able to generate documentation (although that's something to think about), but because
it's the only way to tell both Google's 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-free
compilations.
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 using it a couple of years ago.
So, I've adopted a zero-tolerance policy for warnings: nothing gets checked in if the Closure Compiler
generates even a single warning.
Unfortunately, I'm not sure the [JSDoc](http://usejsdoc.org/) folks and the
[Closure Compiler](https://developers.google.com/closure/compiler/docs/js-for-compiler) are totally in sync on
everything. And then there's [PhpStorm](http://www.jetbrains.com/phpstorm/webhelp/creating-jsdoc-comments.html),
whose code inspections occasionally fail; sometimes a bogus code inspection warning can be fixed with some
additional JSDoc @name or @class annotations, but not always.
In any case, the subset of variable and function type declarations I use works pretty well across the board;
I ignore code inspection warnings in the IDE, as long as they are clearly erroneous (or clearly innocuous).
And finally, speaking of warnings, I've had to tell PhpStorm to "shut up" about a few:
- Unfiltered for…in loop
- Bitwise operator usage
- Comma expressions
- “throw” of exception caught locally
I acknowledge those those features can introduce bugs if you're not careful, so I make sure I'm careful. I don't
subscribe to the dogmatic approach that others (eg, the author of JSHint) 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's
*never* a good idea to cross in the middle sometimes, too.
I've also made the following "weak warnings" instead of "warnings":
- Unused JavaScript / ActionScript local symbol
because it's a useful warning, but I don't like being penalized for functions that have been "prototyped" a specific
way but can't always be implemented exactly as prototyped.
*[@jeffpar](http://twitter.com/jeffpar)*
*September 30, 2014*

View file

@ -38,8 +38,6 @@ var glob = require("glob");
* @class exports
* @property {function(string)} sync
*/
var mkdirp = require("mkdirp");
var HTTPAPI = require("./httpapi");
var DumpAPI = require("../../shared/lib/dumpapi");
var MarkOut = require("../../markout");

View file

@ -962,6 +962,17 @@ HTTPAPI.verifyUserID = function(sUser, res, done)
return true;
};
/**
* getUserDir(sUser)
*
* @param {string} sUser
* @return {string}
*/
HTTPAPI.getUserDir = function(sUser)
{
return path.join(sServerRoot, "/logs/users/" + /* sUser.substr(0, 2) + "/" + */ sUser);
};
/**
* createUserDir(sUser)
*
@ -972,7 +983,7 @@ HTTPAPI.verifyUserID = function(sUser, res, done)
*/
HTTPAPI.createUserDir = function(sUser)
{
var sDir = path.join(sServerRoot, "/logs/users/" + sUser.substr(0, 2) + "/" + sUser);
var sDir = HTTPAPI.getUserDir(sUser);
return (fs.existsSync(sDir) || !!mkdirp.sync(sDir));
};
@ -987,7 +998,7 @@ HTTPAPI.verifyUserDir = function(sUser, done)
HTMLOut.logDebug('HTTPAPI.verifyUserDir("' + sUser + '")');
if (sUser) {
var sDir = path.join(sServerRoot, "/logs/users/" + sUser.substr(0, 2) + "/" + sUser);
var sDir = HTTPAPI.getUserDir(sUser);
fs.exists(sDir, function(fExists) {
if (!fExists) {
HTTPAPI.verifyUserID(sUser, null, function(iVerified, result, res) {

View file

@ -4,13 +4,13 @@ Intel 80286 CPU Documentation
### 80286 Errata
* [ARPL Behavior](arpl/)
* [Coprocessor Operand Beyond Segment Limit](b2_b3_information/#coprocessor-operand-partially-beyond-limit-of-erc-segment)
* [Instructions Longer than 10 Bytes](long_instructions/)
* [Loading Null Selector Values Into DS or ES Registers](b2_b3_information/#loading-null-selector-values-into-ds-or-es-registers)
* [Non-Restartable Protection Violations](b2_b3_information/#non-restartable-protection-violations)
* [POPF Behavior](b2_b3_information/#popf-behavior)
* [REP MOVS and REP INS Restartability](rep_restartability/)
* [Early 80286 Errata of Interest](exceptions_and_early_errata/#early-80286-errata-of-interest)
* [Coprocessor Operand Beyond Segment Limit](b2_b3_info/#coprocessor-operand-partially-beyond-limit-of-erc-segment)
* [Instructions Longer than 10 Bytes](extra_prefixes/)
* [Loading Null Selector Values Into DS or ES Registers](b2_b3_info/#loading-null-selector-values-into-ds-or-es-registers)
* [Non-Restartable Protection Violations](b2_b3_info/#non-restartable-protection-violations)
* [POPF Behavior](b2_b3_info/#popf-behavior)
* [REP MOVS and REP INS Restartability](rep_restart/)
* [Early 80286 Errata of Interest](early_errata/#early-80286-errata-of-interest)
### 80286 Undocumented Opcodes
@ -18,11 +18,11 @@ Intel 80286 CPU Documentation
### 80286 Real-Mode Emulation Notes
* [Executing Real Mode Programs in Protected Mode](executing_real_mode_programs_in_protected_mode/)
* [Discrepancies from an iAPX 86/88 Using Emulation](executing_real_mode_programs_in_protected_mode/#discrepancies-from-an-iapx-86-88-using-emulation)
* [Extending the Address Space of Current iAPX 86 Software](executing_real_mode_programs_in_protected_mode/#extending-the-address-space-of-current-iapx-86-software)
* [Mixing Real Mode and Protected Mode](executing_real_mode_programs_in_protected_mode/#mixing-real-mode-and-protected-mode)
* [Exceptions from Undefined Opcodes and String Instructions](exceptions_and_early_errata/)
* [Executing Real Mode Programs in Protected Mode](real_mode/)
* [Discrepancies from an iAPX 86/88 Using Emulation](real_mode/#discrepancies-from-an-iapx-86-88-using-emulation)
* [Extending the Address Space of Current iAPX 86 Software](real_mode/#extending-the-address-space-of-current-iapx-86-software)
* [Mixing Real Mode and Protected Mode](real_mode/#mixing-real-mode-and-protected-mode)
* [Exceptions from Undefined Opcodes and String Instructions](early_errata/)
### Assorted Publications