JavaScript Tips
1 - Unnecessary DOM Manipulation
The DOM is slow. Limiting your interaction with it will greatly increase your code’s performance. Consider the following (bad) code:
1
2
3
4
5
| // anti-pattern for ( var i = 0; i < 100; i++){ var li = $( "<li>" ).html( "This is list item #" + (i+1)); $( "#someUL" ).append(li); } |
<li/>
elements, and then appends that HTML to the containing element. That way, you jump into the DOM a total of once. Here’s an example:
1
2
3
4
5
| var liststring = "" ; for ( var i = 100; i > 0; i--){ liststring += "<li>This is list item #" + (99- i); } document.getElementById( "someUL" ).innerHTML(liststring); |
1
2
3
4
5
6
7
| var liststring = "<li>" var lis = []; for ( var i = 100; i > 0; i--){ lis.push( "This is list item #" + (99- i)); } liststring += lis.join( "</li><li>" ) + "</li>" ; document.getElementById( "someUL" ).innerHTML(liststring); |
join()
is more efficient than string concatenation. This is one of the fastest and easiest ways to build repetitive HTML in JavaScript without using a template library or framework.2 - Inconsistent Variable & Function Names in JavaScript
This next item isn’t a performance issue, but is extremely important – especially if you are working on code that other people work on, as well. Keep your identifiers (variable and function names) consistent. Consider the following variables as an example:
1
2
3
| var foo = "bar" ; var plant = "green" ; var car = "red" ; |
Something
. This introduces inconsistency in your variable naming pattern, causing your brain to cognitively flag this variable as being different or special. This is why constants in most languages are traditionally defined with all caps.You can take this a step further by maintaining similar length, grammatical structure, and explanatory nature when naming functions. For example, consider the following contrived function:
1
2
3
| function subtractFive(number){ return number - 5; } |
1
2
3
| function addFive(number){ return number + 5; } |
getTweetHTML()
. You might also prepend a function’s name with do
, if the function simply performs an operation and doesn’t return a value, eg: doFetchTweets()
.Constructor functions typically follow the tradition of classes in other languages, capitalizing the first letter:
1
2
3
| function Dog(color){ this .color = color; } |
3 - Use hasOwnProperty()
in for...in
Loops
JavaScript’s arrays are not associative; trying to use them as such is frowned upon by the community. Objects, on the other hand, can be treated as hash tables, and you can iterate over an object’s properties by using the for...in
loop, like so:
1
2
3
| for ( var prop in someObject) { alert(someObject[prop]); // alert's value of property } |
for...in
loop iterates over every enumerable property on the object’s prototype chain. This can be problematic if you only want to use the properties that exist on the actual object.You can solve this issue by using the
hasOwnProperty()
method. Here’s an example:
1
2
3
4
5
| for ( var prop in someObject) { if (someObject.hasOwnProperty(prop)) { alert(someObject[prop]); // alert's value of property } } |
someObject
.4 - Comparing Boolean Values
Comparing boolean values in a condition is a waste of computation time. Take a look at the following for an example:
1
2
3
4
5
| if (foo == true ) { // do something for true } else { // do something for false } |
foo == true
. The comparison of foo
and true
is unnecessary because foo
is already a boolean value (or it’s a truthy or falsey one). Instead of comparing foo
, simply use it as the condition, like this:
1
2
3
4
5
| if (foo) { // do something for true } else { // do something for false } |
false
, use the logical NOT operator, as shown below:
1
2
3
4
5
| if (!foo) { // do something if foo is false } else { // do something if foo is true } |
5 - Event Binding
Events are a complicated subject in JavaScript. Gone are the days of inlineonclick
event handlers (except in some very rare “splash page” cases). Instead, use event bubbling and delegation.Let’s imagine that you have a grid of pictures that need to launch a modal lightbox window. Here’s what you shouldn’t do. Note: we’re using jQuery here, assuming you are using a similar library. If not, the same bubbling principles also apply to vanilla JavaScript.
The relevant HTML:
1
2
3
4
5
6
| < div id = "grid-container" > < a href = "someimage.jpg" >< img src = "someimage-thumb.jpg" ></ a > < a href = "someimage.jpg" >< img src = "someimage-thumb.jpg" ></ a > < a href = "someimage.jpg" >< img src = "someimage-thumb.jpg" ></ a > ... </ div > |
1
2
3
| $( 'a' ).on( 'click' , function () { callLightbox( this ); }); |
#grid-container
element instead.
1
2
3
| $( "#grid-container" ).on( "click" , "a" , function (event) { callLightbox(event.target); }); |
this
and event.target
refer to the anchor element. You can use this same technique with any parent element. Just make sure to define the element that should be the event’s target.6 - Avoid Ternary Redundancy
The overuse of ternary statements is quite common both in JavaScript and PHP.
1
2
| // javascript return foo.toString() !== "" ? true : false ; |
1
2
| // php return (something()) ? true : false; |
true
or false
value, meaning you don’t need to explicitly add true
/false
as ternary values. Instead, you could simply return the condition:
1
2
| // javascript return foo.toString() !== "" ; |
1
2
| // php return something(); |
PHP Tips
7 - Use Ternary When Appropriate
if...else
statements are a central part of most languages. But doing something simple, such as assigning a value to a variable based upon a condition – well, they can junk up your code. Consider the following code:
1
2
3
4
5
6
7
8
| if ( $greeting ) { $post ->message = 'Hello' ; } else { $post ->message = 'Goodbye' ; } |
1
| $post ->message = $greeting ? 'Hello' : 'Goodbye' ; |
As useful as the ternary operator is, the most important guideline is not to over-use it! The goal of coding is not to cramp your logic into as few lines as possible.
8 - Throw Exceptions Instead of Inception-Style Nesting
Let’s face it: many levels of nesting is ugly and difficult to maintain/read. The following code is a relatively simplified example, but they get much worse over time:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
| // anti-pattern $error_message = null; if ( $this ->form_validation->run()) { if ( $this ->upload->do_upload()) { $image = $this ->upload->get_info(); if ( ! $this ->image->create_thumbnail( $image [ 'file_name' ], 300, 150)) { $error_message = 'There was an error creating the thumbnail.' ; } } else { $error_message = 'There was an error uploading the image.' ; } } else { $error_message = $this ->form_validation->error_string(); } // Show error messages if ( $error_message !== null) { $this ->load->view( 'form' , array ( 'error' => $error_message , )); } // Save the page else { $some_data [ 'image' ] = $image [ 'file_name' ]; $this ->some_model->save( $some_data ); } |
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
| try { if ( ! $this ->form_validation->run()) { throw new Exception( $this ->form_validation->error_string()); } if ( ! $this ->upload->do_upload()) { throw new Exception( 'There was an error uploading the image.' ); } $image = $this ->upload->get_info(); if ( ! $this ->image->create_thumbnail( $image [ 'file_name' ], 300, 150)) { throw new Exception( 'There was an error creating the thumbnail.' ); } } // Show error messages catch (Exception $e ) { $this ->load->view( 'form' , array ( 'error' => $e ->getMessage(), )); // Stop method execution with return, or use exit return ; } // Got this far, must not have any trouble $some_data [ 'image' ] = $image [ 'file_name' ]; $this ->some_model->save( $some_data ); |
if
statement. Keep it simple!
9 - False
-Happy Methods
Being exception-happy is far more advantageous than being false-happy.Ruby or Python developers are used to watching for trivial exceptions. While that sound tedious, it’s actually quite a good thing. If anything goes wrong, an exception is thrown, and you instantly know where the problem is.
In PHP – and especially when using older frameworks, such as CodeIgniter – you get what I refer to as “false-happy code” (as opposed to exception-happy). Instead of having an exception get all up in your face, it just returns a
false
value and assigns the error string to some other property. This forces you to fish it out of the class using a get_error();
method.Being exception-happy is far more advantageous than being false-happy. If an error occurs within your code (eg: could not connect to S3 to upload an image, or a value is empty, etc.), then throw an exception. You can also throw specific types of exceptions by extending the
Exception
class, like so:
1
| class CustomException extends Exception {} |
Tip 10 - Use Guard Clauses
It’s common to useif
statements to control a function or method’s execution path. It’s tempting to test a condition and execute a lot of code when the condition results in true
, only to simply return in the else
statement. For example:
1
2
3
4
5
6
7
8
| function someFunction( $param ) { if ( $param == 'OK' ) { $this ->doSomething(); return true; } else { return false; } } |
1
2
3
4
5
| function someFunction( $param ) { if ( $param != 'OK' ) return false; $this ->doSomething(); return true; } |
Tip 11 – Use while
for Simple Iterations
The for
loop is commonly used when you need, for example, a counter. Here’s a simple for
loop:
1
2
3
| for ( var i = 0; i < x; i++) { ... } |
for
loop, but a while
loop may be better if you just need something simple, like this:
1
2
3
4
| var i = x; while (i--) { ... } |
Tip 12 – Keep Methods Maintainable
This is easily one of the most frequent mistakes made by newcomers.A method is an object’s unit of work, and limiting your methods to a maintainable size makes your code easier to read and maintain. Take a look at the following monster method:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
| class SomeClass { function monsterMethod() { if ( $weArePilots ) { $this ->goAndDressUp(); $this ->washYourTeeth(); $this ->cleanYourWeapon(); $this ->takeYourHelmet(); if ( $this ->helmetDoesNotFit()) $this ->takeAHat(); else $this ->installHelmet(); $this ->chekcYourKnife(); if ( $this ->myAirplain() == "F22" ) $this ->goToArmyAirport(); else $this ->goToCivilianAirport(); $this ->aim(); $this ->prepare(); $this ->fire(); } } } |
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
| class SomeClass { function monsterMethod() { if ( $weArePilots ) { $this ->prepareYourself(); $this ->tryHelmet(); $this ->findYourAirport(); $this ->fightEnemy(); } } private function prepareYourself() { $this ->goAndDressUp(); $this ->washYourTeeth(); $this ->cleanYourWeapon(); $this ->chekcYourKnife(); } private function tryHelmet() { $this ->takeYourHelmet(); if ( $this ->helmetDoesNotFit()) $this ->takeAHat(); else $this ->installHelmet(); } private function findYourAirport() { if ( $this ->myAirplain() == "F22" ) $this ->goToArmyAirport(); else $this ->goToCivilianAirport(); } private function fightEnemy() { $this ->aim(); $this ->prepare(); $this ->fire(); } } |
Step 13 - Avoid Deep Nesting
Too many levels of nesting makes code difficult to read and maintain. Consider the following:
1
2
3
4
5
6
7
8
9
10
| function doSomething() { if ( $someCondition ) { if ( $someOtherCondition ) { if ( $yetSomeOtherCondition ) { doSomethingSpecial(); } doSomethingElse(); } } } |
1
2
3
4
5
6
7
8
9
10
11
12
| function doSomething() { if (! $someCondition ) { return false; } if (! $someOtherCondition ) { return false; } if ( $yetSomeOtherCondition ) { doSomethingSpecial(); } doSomethingElse(); } |
When you find yourself with nested
if
statements, closely examine your code; your method may be performing more than one task. Here’s an example:
1
2
3
4
5
6
7
| function someFunc() { if ( $oneThing ) { $this ->doSomething(); if ( $anotherThing ) $this ->doSomethingElse(); } } |
1
2
3
4
5
6
7
8
9
10
| function someFunc() { if ( $oneThing ) { $this ->doSomething(); $this ->doAnotherThing( $anotherThing ); } } private doAnotherThing( $anotherThing ) { if ( $anotherThing ) $this ->doSomethingElse(); } |
Tip 14 – Avoid Magic Numbers and Strings
Magic numbers and strings are evil. Define variables or constants with the values you want to use in your code.Instead of this:
1
2
3
4
5
| function someFunct() { $this ->order->set(23); $this ->order->addProduct( 'superComputer' ); $this ->shoppingList->add( 'superComputer' ); } |
1
2
3
4
5
6
7
| function someFunct() { $orderId = 23; $selectedProductName = 'superComputer' ; $this ->order->set( $orderId ); $this ->order->addProduct( $selectedProductName ); $this ->shoppingList->add( $selectedProductName ); } |
While some might argue that we’re needlessly creating variables, the performance hit is negligible. Readability always takes priority. Remember: don’t optimize for performance until you can describe why it’s necessary.
Step 15 - Use Built-In Array Functions
Use the built-in array functions instead offoreach()
.Not Ideal:
1
2
3
| foreach (& $myArray as $key => $element ) { if ( $element > 5) unset ( $myArray [ $key ]); } |
1
| $myArray = array_filter ( $myArray , function ( $element ) { return $element <= 5;}); |
Tip 16 - Don’t Overuse Variables
It’s easy to overuse variables, but remember that variables are stored in memory. For every variable you create, the system needs to allocate memory for that variable. Look at this code:
1
2
3
4
5
| public function get_posts() { $query = $this ->db->get( 'posts' ); $result = $query ->result(); return $result ; } |
$result
variable isn’t necessary. The following code omits that variable:
1
2
3
4
| public function get_posts() { $query = $this ->db->get( 'posts' ); return $query ->result(); } |
General Programming Recommendations
Tip 17 - Rely on the Database Engine
Anything less is a code smell.A database is designed for working with data; use its tools and abilities to make your application more efficient.
For example, you can avoid redundant database queries in many circumstances. Most plug-and-play user management scripts use two queries for user registration: one to check whether the e-mail/username already exists and another to actually add it to the database. A much better approach is to set the username field to
UNIQUE
. You can then use native MySQL functions to check whether or not the record was added to the database.Tip 18: Properly Name Your Variables
The days of naming your variablesx
, y
, z
are over (unless, of course, you’re dealing with a coordinate system). A variable represents an important part of your logic. Don’t want to type a long name? Get a better IDE. Modern IDEs auto-complete variable names in a blink of an eye.
Always be coding for six months from now. Are you certain that you’ll remember what that $sut
variables refers to a year from now? Likely not: be descriptive. Anything less is a code smell.
Tip 19 - Methods Represent Actions
Mistakes happen; the key is to learn from them.Name your methods with verbs representing the action they perform. The main concept is the exact opposite of the variable naming scheme. Use a short, but descriptive, name in a large scope (ie: public methods), and use a longer and more detailed name in a short scope (ie: private / protected methods). This helps make your code read like well written prose.
Also avoid any language other than English, when naming your methods. It’s annoying to read function names like 做些什麼() or делатьчтото() in your project. It may be impossible for other programmers to understand your intent. While it might seem arrogant, for better or worse, English is the adopted language of code. Try to use it, if we’re working on a large team.
Tip 20: Structure Recommendations
Finally, code structure is just as important to readability and maintainability as anything else we’ve talked about today. Here are two recommendations:- Indent with four or two space-width tabs. Anything more, such as eight spaces, is too much and will make your code difficult to read.
- Set a reasonable line-width and respect it. Forty characters in a line? We’re not in the ’70s any more; set your limit to 120 characters, put a mark on the screen, and force yourself or your IDE to respect that limit. 120 characters gives you a nice width without making you scroll.
Conclusion
“I’ve never made a stupid programming mistake.” — No one, ever.Mistakes happen; the key is to learn from them. We at Nettuts+ have made, and will continue to make, mistakes. Our hope is that you learn from our mistakes so that you can avoid them in the future. But, to be honest, the best way to learn best practices is to make the mistakes yourself!
Thanks for reading!