• If you are citizen of an European Union member nation, you may not use this service unless you are at least 16 years old.

  • Finally, you can manage your Google Docs, uploads, and email attachments (plus Dropbox and Slack files) in one convenient place. Claim a free account, and in less than 2 minutes, Dokkio (from the makers of PBworks) can automatically organize your content for you.

View
 

JavaScript Style Guide

This version was saved 12 years, 4 months ago View current version     Page history
Saved by Charles Jolley
on February 4, 2009 at 5:21:49 pm
 

SproutCore Coding and Naming Convention Style Guidelines

 

The SproutCore coding and naming convention style guidelines are adapted from the on the Cocoa style guide from CocoaDevCentral. SproutCore, being a framework that is based on JavaScript, not everything in the aforementioned style guide applies completely. However, since SproutCore has a lot of conceptual similarities with Objective-C, it is useful to align the coding styles somewhat. It not only allows for the similar concepts to be described in the same way, but also it makes it easier for developers using both Objective-C and SproutCore to go from one to the other. Plus, the Cocoa coding style guidelines are widely followed and therefore there is a pool of developers already familiar with it.

 

This coding style guide only applies to how to structure and name your classes, variables, functions, etc, not on how to document them. Please refer to the next section for documentation style guide lines. 

 

Classes

 

Although JavaScript is prototypical, SproutCore overlays the some of the concepts of classes for ease of use.

 

Defining Classes 

Just as with Objective-C, JavaScript and thus, SproutCore, is not natively name spaced. Therefore, it is very important to be careful to naming your classes. When you create a SproutCore application using the build tools, it will automatically set up a name space variable for you in core.js in your project. 

 

For example, if you are writing an application called "AddressBook", the build tools will create the following in core.js:

 

AddressBook = SC.Object.create();

 

In SproutCore, class names are always capitalized. If you are extending an existing SproutCore view, it is generally preferred to add the name of the view to the end of the class name. For instance, if you extend a CollectionView in your AddressBook application to show a list of Contact objects, you would do it in the following manner:

 

AddressBook.ContactCollectionView = SC.CollectionView.extend({

// Add your custom properties and functions here. 

});

 

Instantiated Classes

If you are creating an instantiated SproutCore class, such as a controller, the naming convention is slightly different. An instantied class is named using camel case with the first letter lowercase. If you're creating an ArrayController to control your Contact objects, you would do the following:

 

AddressBook.contactsController = SC.ArrayController.create({

     // Add your custom properties and functions here. 

});

 

Create versus Extend

You may have noticed that the first two examples use .create() and the third uses .extend(). When should you use one of the other?

 

When you use  .create(), you are creating an instance of a class. It is similar to using the new operator. On the other hand, when you use .extend(), the resulting object is created as a new a subclass, where the properties and functions from the base class are added in addition to the things that you define on your own.

 

 

Properties and Variables

 

Descriptive property and variable names are strongly encouraged in SproutCore. Since most deployed applications will deliver the JavaScript compressed to the user, using terse names will not significantly reduce the amount sent over the wire. Also, if any obfuscation is used as part of the build process, it will optimize the names for you. Therefore, naming your variables or properties as descriptive as you need will make the life of the developer a lot better and not degrade performance or the user experience. 

 

All properties and variable names are camel case with the first letter lower case. The name should describe what the variable or property contains. It is not necessary to indicate the type of a variable in the name. For example, you don't need to name a variable meant to store a string myString or an array variable myArray, but it is useful to name them in such a way that it is obvious anyway. 

 

The only exceptions are booleans. When naming a boolean, it is recommended prefix them with is or isNot. Therefore, it is easy to determine in which manner they are to be used.

 

isEnabled: true // This is good.

 

enabled: true // This is good, but possibily confusing.

 

Properties

A property is a variable that is directly defined on an object or class. These properties can be either public or private. Since JavaScript has no actual concept of private properties,  the concept is merely to help developers. A private property name is prefixed with a underscore. In the following example of the contactController, there are two properties, first there is property called contactsList that is a public array containing the list of contacts, and then there is a private property called _contactsCount for internal book keeping. No matter if the property is static or if it is a computed property (which is actually a function), the naming convention is the same. 

 

AddressBook.contactsController = SC.ArrayController.create({

 

_contactsCount: 0, // This is a private variable.

 

// This is a public variable.  Access using get() and set() 

contactsList: []

});

 

A note about accessing properties

To access or modify public properties inside or outside the object, always use the get and set functions on the object, i.e.: this.get('contacts'). For private properties, always access them directly (i.e., this._contactsCount = 23;)  If you find yourself accessing private variables from other objects, make them public and use get and set to access them.

 

You can also use the getPath function if you need to access a deeper property. This is not as fast as using get, but it can make your code more readable:

 

     this.getPath('content.nameComponents.prefix');

 

Variables

Variables are internal to a function only. Since it is local to that function, it is not encouraged to use an underscore as a prefix.

 

JavaScript will allow you to reference any variable name, even if you have not declared it before.  If you reference a variable without declaring it first, however, the variable will be treated as a "global" variable, accessible to every function in your application.  Since SproutCore applications are often very complex, it is important that you always use the var statement to declare your local variables first.  Otherwise you may introduce bugs inadvertently. Mistakenly defining something outside of its scope is a typical error that can be very difficult to debug.

 

In addition, local variables are the fastest way of storing data in JavaScript.  Making extensive use of local variables will get the best performance out of your code.

 

For example, in this function, count is a local variable:

 

countContacts: function()

{

var count = 0; // This is local.

}

 

 

However, in this function, count is not a local variable, but rather in the window scope:

 

countContacts: function()

{

count = 0; // This is NOT local.

}

 

Optimizing the use of variables

When you access a property defined on an object multiple times, it is encouraged to store it locally inside the function. For instance, if you have a function that gets content of a view, collects some information from the record and then returns a string with all of the values combined.  The first example is not efficient because there are many unnecessary function calls. Not only that, it is also hard to read! The second example pulls the content object into a local variable, contact and constructs the string with far fewer function calls and with far superior readability to boot. 

 

This is confusing and inefficient:

 

constructName: function()

{

return [this.get('content').get('prefix'),this.get('content').get('firstName'), this.get('content').get('middleName'), this.get('content').get('lastName'), this.get('content').get('suffix')].join(' '); 

}

 

This is more readable and far faster since the commonly accessed property is saved locally:

 

constructName: function()

{

var contact = this.get('content');

 

// This is more readable and far faster.

return [contact.get('prefix'),contact.get('firstName'), contact.get('middleName'), contact.get('lastName'), contact.get('suffix')].join(' ');  

}

 

This is best (using the getEach function):

 

constructName: function()

{ 

// This is EVEN more readable.

return this.get('content').getEach('prefix','firstName',

       'middleName','lastName','suffix').join(' ');  

}

 

Another suggestion declare all local variables at the beginning of your method. The YUI compressor recommends declaring all variables on one line i.e., 

 

     var a, b, x, obj;   

 

Functions

 

This is perhaps where the naming and coding style guidelines for SproutCore deviate the most from Objective-C. The reason is that the syntactic sugar is very different between Objective-C and JavaScript. 

 

For SproutCore applications, the naming conventions more or less the same as how to name properties. Just as with properties, private functions on objects are prefixed with an underscore. The name of a function should be descriptive and give a sense of what purpose it is intended to perform. This, however, is not a blank check to make a function name a full sentence. Naming a function constructContactFullNameWithPrefixAndSuffixString is not helping anyone, especially you, the developer. The specifics can be captured in the documentation. Instead, naming the function constructName gives a hint at what the function does and is not exceedingly verbose.

 

Observers

If you are creating a function that serves as an observer, it is not required, but strongly encouraged to add the DidChange suffix to the name. Also, if the observer function is only observing one property, it is nice to name it after the name of the observed property. Below is an observer function observing the isSelected boolean property of a view:

 

isSelectedDidChange: function()

{

// Do stuff based on selection.

}.observes('isSelected')

 

If you are observing more than one property, it is not useful to name the function after all the properties. In this case, naming the observer function after the first observed property is acceptable but it also okay to name something descriptive that hints at what happens in the function when it is triggered. For example, if you are observing multiple properties, such as content, isSelected, and dimensions and when any of those change, a redraw of your view will be triggered, you can name it as such:

 

redrawStateDidChange: function()

{

// Redraw the view.

}.observes('content', 'isSelected', 'dimensions')

 

Bindings

Bindings are special properties that will connect together the values of two objects in your application.  Unlike most parts of SproutCore, you must follow a very specific formula for bindings to work properly.  To bind to the value property of one object from the title object of another property, you must add a property to the object you are binding TO that looks like this:

 

     valueBinding: "MyApp.anotherObject.title"

 

 

Other Coding Style Guidelines

 

SproutCore does not have a lot of specific coding style guidelines but the ones that it does have helps a SproutCore application be readable, maintainable, and easily extended. 

 

Two Space Soft tabs!

One very important, yet easy way to ensure that your code is readable is to have a consistent code indent scheme. SproutCore's standard is to have a two space soft tab instead of an actual tab. This makes the code much more readable and it is easy to set up your favorite text editor to do this.

 

Value vs Content

Usually a property named value represents a simple JavaScript value of an object, such as a string, a number, etc.  A property named content (or ending in Content) represents an Object that you will show one or more aspects of.  For example, if you set the value property on an SC.LabelView to a string, the label view will show that string directly.  If you set the content property on the SC.LabelView to a Contact object and then set the contentValueKey to fullName, then the SC.LabelView will show the value of the fullName property on the content object.

 

Always use ===

You should always use ===  and !== when comparing objects.  This special form of equality is both faster and performs fewer transforms on the values you are comparing.  It will behave more like you would expect compare to other languages than == or !=.  If you want to see if a value is either null OR undefined, use SC.none(foo).  

 

Cache Length in For Loops

When writing for() loops, first capture the length you want to iterate through like so:

 

var len = items.length;

for(var idx=0;idx<len;idx++) { item = items[idx]... } 

 

Better yet, if the order you iterate does not matter, use a while loop like so since it is faster than using a for loop: 

 

var idx = items.length;

while(--idx >= 0) { ... }

 

Always Use Curly Brackets for If statements

It is OK to use the curly brackets after a while, for, or if statement but ONLY if the following bit fits on the same lime.  Never, ever, separate while, for, or if statements from their predicated onto separate lines without wrapping in curly braces.  This creates lots of programming errors:

 

if (isReady) readyCount++;  // OK - one line

 

if (isReady)

 readyCount = oldReadCount + newReadyCount; // not OK!

 

if (isReady) 

{

readyCount = oldReadyCount + newReadyCount ; 

} // OK - multi-line but wrapped in curly-Qs.

 

Also, in these coding examples, you have have noticed that the {} are on new lines, that is due to the author's stance and not an official guideline. That is up to the developer to choose how to format your curly-braces since after it is compressed, it won't really matter! 

Comments (0)

You don't have permission to comment on this page.