General
Any violation to these standards is allowed if it enhances readability.
This guide serves as the coding standard for all Appcelerator JavaScript code including the Mobile Web platform, Titanium Mobile project templates, KitchenSink, and all other sample code.
- All variables, functions, methods, class names, and comments SHOULD be written in English.
- Line length SHOULD be limited to a "screen width" which can vary between 100 and 200 characters.
- BE CONSISTENT.
Files
- Filenames SHOULD be all lower case if the file does not contain any instantiable objects (classes).
- Filenames SHOULD be CamelCase if the file contains an instantiable object (class).
- Directories SHOULD be all lower case and unless the directory serves as a part of a module path (i.e. /path/to/Ti/Filesystem).
Variables
- Public variables SHOULD be mixedCase (i.e. containerNode, firstItem, etc).
- Private variables SHOULD be _underscoreMixedCase (i.e. _position, _layerIdx).
- Constants SHOULD be UPPER_CASE.
- All variables SHOULD be defined at the top of the function with a single "var" keyword.
- Global variables SHOULD be kept to a minimum to avoid naming collisions and scope lookups.
- Variables SHOULD be descriptive, but short names are acceptable in small functions.
- Variable names MUST NOT be reserved words or web browser built-in objects: window, document, etc..
- Complementary names MUST be used for complementary entities (i.e. get/set, add/remove, create/destroy, start/stop, etc).
Functions
- Public functions and methods SHOULD be mixedCase (i.e. myFunction, doSomething, etc).
- Private functions and methods SHOULD be _underscoreMixedCase (i.e. _init, _destroyUI, etc).
- Anonymous functions SHOULD be named for easier debugging (names would be removed during minification).
- Nested functions ARE permitted.
- Closures ARE permitted, but caution is advised to avoid memory leaks.
- Method names SHOULD be verbs or verb phrases (i.e. getValue(), isEnabled()).
Objects
- Methods SHOULD be defined on the object's prototype, not via "this" in the constructor.
- Array and object properties SHOULD be initialized in the constructor, not in the prototype.
- Array and object properties MUST NOT be added to the prototype.
- Properties that are boolean-ish and default to falsey (false, null, undefined, 0) do not NEED to be defined before use.
Modules
- Modules MUST follow the CommonJS modules or AMD (asynchronous module definition) specification.
- Module IDs MUST NOT start with a forward slash.
- Module IDs MAY contain periods.
- Module IDs MUST NOT be named "require", "exports", or "module".
- Module names SHOULD NOT begin with a number.
- AMD modules SHOULD NOT provide a name to define() and let the loader automatically detect it.
- AMD modules SHOULD NOT contain more than one module definition, though it is possible.
- AMD modules MUST NOT contain more than one anonymous module definition.
Whitespace
- Tabs (set to 4 spaces) MUST be used for indentation rather than spaces.
- Blank lines SHOULD NOT contain any tabs or spaces.
- Blank lines SHOULD be used to separate blocks of logic.
- Commas SHOULD be followed by a space.
-
Ternary operators (inline-ifs) SHOULD have spaces around both the ? and : operators.
drink = (location ==
"scotland"
) ?
"whisky"
:
"whiskey"
;
- Semi-colons in for loops SHOULD be followed by a space.
- There SHOULD be a space around the outside of the parentheses in if, switch, catch, while, and for constructs.
- Operands and operators SHOULD be separated by spaces.
- Statements MAY be aligned wherever it improves readability.
Braces
- All block structures including if, else, switch, try, catch, function, while, for, and so on MUST use braces around body.
- Opening braces SHOULD be at the end of the first line of the block statement.
- Closing braces SHOULD be on a separate line and indented to match indentation of the opening brace's line.
if (error) { crashAndBurn(); } |
Semicolons
- All lines of code MUST end with one and only one semicolon.
- A compile-time JavaScript minifier may remove semicolons to reduce file size.
Loops
- Generally, index variables SHOULD be defined with all other variables at the top of the function.
- Empty for and while loops SHOULD have empty braces or a semicolon at the end of the line.
- When looping over arrays, for loops SHOULD pre-determine the length (i.e. for (var i = 0, len = arr.length; i < len; i++) {}).
- for loops SHOULD be used instead of for-in loops when looping over arrays.
- for-in is acceptable for looping over objects, though it is preferred to use the ECMAScript 5 Object.keys() method.
- You SHOULD use a for loop instead of Array.forEach().
- The overhead of calling a function for each items is significant.
- Iterator variables SHOULD be named i, j, k, etc.
Conditionals
- Short circuiting conditionals are acceptable (i.e. hasX && doSomething()).
- The last statements in a switch SHOULD NOT end with a break statement.
- switch cases MAY be broken with either a break or return.
- Ternary operators should be used for simple code conditions (i.e. var y = x ? 1 : 2; isEnabled() ? doSomething() : doSomethingElse();).
- Use !~ with an indexOf() to check if it contains a value.
- Use !(a<b || b<a) to check if two dates or arrays are equal.
Data Types
- Non-boolean variables SHOULD be cast to boolean using !! or ! operators.
- Non-integer variables SHOULD be cast to integer using |0 (i.e. "123.4"|0 outputs 123).
- Non-string variables SHOULD be cast to strings using ''+ (i.e. ''+123 outputs "123").
- Multiline strings SHOULD be broken up into separate concatenated strings instead of using backslashes at the end of lines.
- Strings SHOULD generally use single quotes, though double quotes are acceptable unless the string contains zero or one characters or contains HTML/XML/JSON codes.
- Literals SHOULD be used instead of constructors (i.e. use [] instead of Array(), {} instead of Object(), '' instead of String()).
- Object literal keys SHOULD only use quotes if the key is a reserved word or contains a space.
- Custom object MAY have toString() methods as long as they return a string without error.
- Arrays and objects SHOULD NOT have hanging commas.
- Floating point constants SHOULD be written with a digit, followed by a decimal point, and at least one decimal (i.e. 1.0).
Allowed Constructs
- Use built-in standard functions (i.e. string.charAt(3) instead of string[pe:3]).
Disallowed Constructs
- with statements ARE NOT permitted.
- You SHOULD NOT modify prototypes of internal objects.
- Possible exception when trying to shim a standardized function that is missing in the given implementation.
- You SHOULD NOT use parenthesis when using delete, typeof, void or calling return, throw, case, in, or new.
- You SHOULD NOT use commas at the beginning of a line; they should be at the end of the line.
Comments
-
Single line comments MUST use C++ style single-line comments, introduced by two slashes:
// my comment
-
Multi-line comments MUST use C-style comments, beginning with /* and ending with */:
/* my multi-
line comment */
Documentation
- Code SHOULD be documented using JSDoc annotations (https://github.com/jsdoc3/jsdoc).
Exception Handling
- try/catch SHOULD be used instead of return codes for complex routines.
- Thrown exceptions SHOULD use built-in exception types: a string, Error,
EvalError, RangeError, ReferenceError, SyntaxError, TypeError, URIError.
- Custom exceptions are acceptable as long as they define their own toString() function.
26 コメント
Tim Poulsen
"Method names SHOULD be verbs or verb phrases (i.e. getValue(), isEnabled())" – except that get/set are reserved on iOS and can cause app crashes. You need to use some other verb, like grabValue()
"Blank lines SHOULD NOT contain any tabs or spaces" – except that most editors, including Studio insert these for you and I'm not going to go through the pain of deleting them.
"You SHOULD NOT modified prototypes of internal objects" – I'm not sure I would agree with the SHOULD NOT unless by "internal objects" you mean Titanium objects. Extending Strings and Numbers with custom methods can be incredibly handy, though agreeably "magic" – in Android at least, such prototype modifications have to be done within each module; on iOS you can do it once and it's accessible throughout the app.
Chris Barber
So are you saying that you cannot on iOS create any function (global or otherwise) that starts with "get" or "set"?
I'm very much against modifying built-in prototypes. This is very bad because there could be collisions with implementations between environments and can complicated debugging.
Tim Poulsen
Best to see Blain for the full story, but my understanding is that on iOS any getFoo / setFoo functions are assumed to be calls to native land and passed across the bridge. I know from my own coding that using get/set in names quite frequently causes a crash on iOS.
And yes to the second part, modifying the Titanium prototypes is generally verbotten. You can add a property or two (like adding property for a database primary key to a table row object). Adding methods is an express ride to crashland (again, mostly on iOS).
Chris Barber
Just found it the get/set thing has been fixed in master (2.1.0). Not sure if it'll get backported to 2.0.2. In any case, moving forward, functions should use verbs or verb phrases.
Stephen Tramer
This has been fixed in master and automatic get/set routing should be considered a "misfeature" that will be removed at some point.
Patrick Seda
For completeness, under "Data Types" maybe String should be added to the Literals:
Literals SHOULD be used instead of constructors (i.e. use [] instead of Array(), {} instead of Object(), '' instead of String())
Rick Blalock
I love this. This is great. There's a couple small preferential things I don't like but really....I don't care. I'd follow this guide in a heartbeat if it means everyone starts using it tomorrow. Great job!
I agree with Patrick - we need to have that as guidelines.
What about ternary operators?
Chris Barber
Sorry, I identified them as "inline-ifs". Not sure where I picked up that lingo. I've see iif() before, but in any case, I'll change the verbiage to "ternary operators" since it sounds nerdier.
Kevin Whinnery
I think this is very good. I personally don't care for declaring all variables at the top of the function. I get that they may be hoisted there anyways, but for readability I prefer to declare the variables in context within the function. Other than that I am pretty much down with the whole thing.
Vasyl Zuzyak
I support accessing chars via charAt() built-in function. But I do not understand why you suggest to use "123.4"|0 expression instead of built-in parseInt() function
Bryan Hughes
Code size. The former is shorter than the latter, and if you are targeting Mobile Web, keeping your code size as small as possible is absolutely critical to getting decent performance (like any web page really).
Vasyl Zuzyak
"replacement" like this should be done by tools like yahoo js compressor. Using tricks in code you forces persons that do not know this hack "evaluate" it. while parseInt() means what it means
Chris Barber
I disagree. Not all instances of parseInt() can be replaced with |0 by a minification tool. It's up to the developer to choose the best method. Most developers are unaware of the |0 trick. Frankly, when you see the pattern often enough, it becomes second nature to identify what the code is doing. The spec is simply implying that you SHOULD use |0, but it's not strictly enforced.
Vasyl Zuzyak
My prev comment is a bit confusing.I mean that
* developer should not do a task (minifying) that should be done by tool.
* developer should use codestyle that will allow to understand his code as many developers as possible
* codestyle that you see under 'view source' command in browser is *separate* story.
Bryan Hughes
In an ideal world I would agree with you, but in practice minifiers are far from perfect. There are many things they can't do, and many things they miss. If you write your code such that you expect them to do everything, your code size will suffer as a result.
Chris Barber
If you have a variable, whether it's a string, number, float, undefined, or null, will automatically be casted as an int if OR'd with 0. It's less bytes. parseInt() is handy if you want to change the base of the number (i.e. from base 10 to base 16).
Vasyl Zuzyak
or helps in case of undefined or null. so it can be applied to parseInt() too. also parseInt() does his job for things like this '10 px', while your expression fails. See my reply to Bryan about less bits
Chris Barber
You are correct. You still need parseInt() for strings that contain text characters. For floats, there are no tricks that I'm aware of and you'll need to use parseFloat(). It's also acceptable at times to use a regular expression to parse numbers out of a string.
Vasyl Zuzyak
>> regular expression to parse
btw maybe we need to add a suggestion not to use regexps where its possible. its 'expensive' operation
Bryan Hughes
Question: what is the preference for ident names with acronyms? Proper camel case says that you should go, for example, myId, not myID, but most people do myID.
Carl Orthlieb
Question: is any of this for Ti applications vs straight JS? For example, there are localization best practices that we should consider wrt strings.
Chris Barber
This style guide is meant to be generic and apply to any type of JavaScript project. WRT i18n/l10n, not all JavaScript environments have localization mechanics, so I felt it was outside the scope of this style guide. Despite Titanium's JavaScript environment having support for i18n/l10n, whether or not to use those functions falls more into a Titanium best practices guide.
Ronen Botzer
Chris, thanks for putting this together. That's a well thought-out document. In a future iteration can these points include a code sample that can be expanded or hidden?
Tim Poulsen
If this document (or a version of it) will be copied to the docs.appcelerator.com site, then we can't use any of the Confluence macros. They don't translate to the TiDuck (JSDuck) format.
Sandeep Bhimavarapu
With respect to concatenating the strings (point 10 under Variables), it looks like string concatenation is faster using '+' operator rather than array.join().
Please see the following link
http://jsperf.com/array-join-vs-string-connect
Chris Barber
You are correct. I will remove that line. I don't see the need to explicitly state that strings should be concatenated using + since the array.join() method is unintuitive. The whole reason for the array.join() method was because of Internet Explorer.