9/10/10

How Good C# Habits can Encourage Bad JavaScript Habits: Part 1

This is the first post in a multi-part series covering common mistakes C# developers tend to make when they first start writing JavaScript.

Introduction
Many people come to jQuery and believe that their knowledge of a previous classical language (C#, Java, etc) will help them be successful at client-side scripting. You can use your classical language skills to accomplish a large amount of functionality with jQuery. However, the more client-side code you write you will find yourself uncovering strange bugs because you didn’t take adequate time to learn JavaScript properly.
“…it turns out that if you have absolutely no idea what you’re doing in the language you can still generally make things work.” –Douglas Crockford, Yahoo!’s JavaScript Architect, Douglas on JavaScript -- Chapter 2: And Then There Was JavaScript

This article is targeted for developers that use jQuery but haven’t invested the time necessary to learn JavaScript. The intent is to help you avoid some common mistakes when moving from a classical language (Java, C#, etc) to JavaScript.

jQuery is a library that is written in JavaScript. It is important to remember that you will always be writing in JavaScript when using jQuery. It is inevitable that you will run into native JavaScript concepts that are foreign to the classical language proficient developer. Taking the time now to be proficient in JavaScript will increase your client-side code quality, efficiency, and decrease code maintenance.

Bad Practices
1. Having Variables & Functions in Global Scope
A best practice in C# is to limit the use of global variables. This doesn’t necessarily translate into a bad practice in JavaScript, but most C# developers are not aware of how easily it is to pollute the global namespace with needless variables and functions.

One way you can pollute the global namespace is to not declare your variables. In JavaScript, if you don’t declare your variables then they become globally available to the rest of the program, which is probably not what you wanted and generally a bad idea.

Coming from a C# background, you are most likely used to the concept of namespacing your classes so they won’t clash with other libraries. In the same way you should be mindful to not declare variables or functions in the global namespace to prevent clashing with other libraries, browser extensions, and also your own code.

Bad Practice
The following code shows some bad examples of how to declare variables and functions.
var iAmGlobal = "Keep these to a minimum";

iAmGlobalToo = "Bad";

function oldSchoolWay() {
//Placed in global scope when executed
iAmGlobalNooo = "Really Bad";

console.log( "Bad oldSchoolWay Function" );
}

//Bad way to prevent namespace clashing
function namespace_oldSchoolWay() {
console.log( "Worse oldSchoolWay Function" );
}

//Functions
window.oldSchoolWay();
window.namespace_oldSchoolWay();

//Global variables are available off window object
console.log( window.iAmGlobal );
console.log( window.iAmGlobalToo );
console.log( window.iAmGlobalNooo );

You can execute and modify the above code from jsFiddle.

In the above code snippet you’ll see that the iAmGlobal, iAmGlobalToo, and iAmGlobalNooo are all global variables. You would expect iAmGlobal and probably iAmGlobalToo to be global variables, but the variable inside the oldSchoolWay function is also global because it was never declared! The best way to stay out of this trouble is to be disciplined and declare all of your variables and not let JavaScript do it for you.

Klaus Komenda actually refers to the function statement as the Old School Way of function declaration. I’ve even seen C# developers trying to prefix their function names (as shown above by namespace_oldSchoolWay) to minimize collisions with other libraries, but you should not do this. That technique still clutters the global namespace.

There are many options to fix this problem, but the simplest is to create a namespace object and then declare all your functions and properties within it. See the following code snippet for a slightly better approach.

Object Literal (All Public)
An Object Literal is a convenient way to create new objects. The syntax looks very similar to what you might expect to see in JSON (JavaScript Object Notation), but they are actually quite different. For more information about this you can check out an in-depth discussion on it by Ben Alman entitled There's no such thing as a "JSON Object".

When you create an Object Literal you can provide properties and methods and they can refer to each other. You can also add additional methods and properties to the object after it has been defined. All the properties and methods that you declare in the Object Literal are publicly accessible to the rest of your JavaScript code.
//Object Literal declaring properties and methods
var skillet = {
//public property
ingredient: "Bacon Strips",

//public method
fry: function() {
console.log( "Frying " + this.ingredient );
}
};
console.log( skillet.ingredient ); //Bacon Strips
skillet.fry(); //Frying Bacon Strips

//Adding a public property to an Object Literal
skillet.quantity = "12";
console.log( skillet.quantity ); //12

//Adding a public method to an Object Literal
skillet.toString = function() {
console.log( this.quantity + " " +
this.ingredient );
};
skillet.toString(); //12 Bacon Strips​

You can execute and modify the above code from jsFiddle.

The above code declares a skillet variable and sets it to an Object Literal with one property (ingredient) and one method (fry) that are both publicly accessible off of the object. You can also add additional public methods and properties to the object after the initial declaration as shown above when adding the quantity property and the toString method. The toString method is able to access the properties of the skillet object since all of it’s parts are public.

Pros
•Cleans up the global namespace by adding a namespace to your properties and methods
•You can add functionality to the object literal at a later point
•All the properties and methods are public

Cons
•Uses the Object Literal syntax to define your properties and methods that some may find cumbersome
•There isn’t the ability to have private properties or methods

Self-Executing Anonymous Function: Part 1 (All Private)
Another common technique that you can use to protect the global namespace is to use a Self-Executing Anonymous Function. This is just a fancy term to refer to a function with no name that is immediately executed after it’s defined. The value of this technique is that you can create a wrapper around your code that protects it from the global namespace. By using the most simple version of the Self-Executing Anonymous Function you can create code that is exclusively private.
//Self-Executing Anonymous Function: Part 1 (All Private)
(function() {
//private variable
var ingredient = "Bacon Strips";

//private function
function fry() {
console.log( "Frying " + ingredient );
}

fry();
}());

//Variables not accessible
console.log( window.ingredient ); //Undefined

//Functions not accessible
try {
window.fry(); //Throws Exception
} catch( e ) {
//Object [object DOMWindow] has no method 'fry'
console.log( e.message );
}

//Can't add additional functionality

You can execute and modify the above code from jsFiddle.

The above code snippet wraps the ingredient variable and fry function with a Self-Executing Anonymous Function and right before the function ends it executes the fry method. After the Self-Executing Anonymous Function finishes executing there is no way to run the function again or reference any of the internals of the wrapped code.

Pros
•Hides all your implementation from external code
•The function runs once and only once
•The code inside doesn’t use the Object Literal notation

Cons
•All information is hidden, which may not be what you want
•Slightly more complicated technique, but not really

Revealing Module Pattern (Public & Private)
The Revealing Module Pattern actually uses the concept of the Self-Executing Anonymous Function as well, but in this case we save the result into a variable. This pattern introduces the concept of a Closure. A closure is a way to keep variables alive after a function has returned. The Mozilla Developer Network has a great page explaining closures. In it they provide a core definition:
“A closure is a special kind of object that combines two things: a function, and the environment in which that function was created. The environment consists of any local variables that were in-scope at the time that the closure was created.”

In the following example we will declare private properties and methods as well as public properties and methods.
//Revealing Module Pattern (Public & Private)
var skillet = (function() {
var pub = {},
//Private property
amountOfGrease = "1 Cup";

//Public property
pub.ingredient = "Bacon Strips";

//Public method
pub.fry = function() {
console.log( "Frying " + pub.ingredient );
};

//Private method
function privateWay() {
//Do something...
}

//Return just the public parts
return pub;
}());

//Public Properties
console.log( skillet.ingredient ); //Bacon Strips

//Public Methods
skillet.fry();

//Adding a public property to a Module
skillet.quantity = 12;
console.log( skillet.quantity ); //12

//Adding a public method to a Module
skillet.toString = function() {
console.log( skillet.quantity + " " +
skillet.ingredient + " & " +
amountOfGrease + " of Grease" );
};

try {
//Would have been successful,
//but can't access private variable
skillet.toString();
} catch( e ) {
console.log( e.message ); //amountOfGrease is not defined
}

You can execute and modify the above code from jsFiddle.

Inside of the Revealing Module Pattern we declared a pub object that will keep all the public properties and methods that you want to be exposed. At the end of the Module the pub object is returned, which is what causes the them to be public outside of the Self-Executing Anonymous Function. All the parts that were not added to the pub object remain private to the outside world; however, the public methods that were exposed have access to the private parts because of the Closure that was created.

Pros
•Allows for public and private properties and methods
•The Technique is Easy to understand
•The code inside doesn’t use the Object Literal notation

Cons
•Doesn’t allow for a way to add private properties to be used in new public methods

Self-Executing Anonymous Function: Part 2 (Public & Private)
We looked at the Self-Executing Anonymous Function earlier as a pattern you could use to keep all your code private. As it turns out, you can actually modify the pattern somewhat so that you can achieve the same benefits of the Revealing Module Pattern. Not only can we achieve public and private properties and methods, but we can also provide an easy way to extend the namespace while keeping the content protected from the global namespace. In addition, the following pattern can protect the $ from conflicting with other JavaScript libraries and also protect undefined from being redefined.

Take a look at the following example, and we will walk through the code explaining the key changes to the pattern.
//Self-Executing Anonymous Func: Part 2 (Public & Private)
(function( skillet, $, undefined ) {
//Private Property
var isHot = true;

//Public Property
skillet.ingredient = "Bacon Strips";

//Public Method
skillet.fry = function() {
var oliveOil;

addItem( "\t\n Butter \n\t" );
addItem( oliveOil );
console.log( "Frying " + skillet.ingredient );
};

//Private Method
function addItem( item ) {
if ( item !== undefined ) {
console.log( "Adding " + $.trim(item) );
}
}
}( window.skillet = window.skillet || {}, jQuery ));

//Public Properties
console.log( skillet.ingredient ); //Bacon Strips

//Public Methods
skillet.fry(); //Adding Butter & Fraying Bacon Strips

//Adding a Public Property
skillet.quantity = "12";
console.log( skillet.quantity ); //12

//Adding New Functionality to the Skillet
(function( skillet, $, undefined ) {
//Private Property
var amountOfGrease = "1 Cup";

//Public Method
skillet.toString = function() {
console.log( skillet.quantity + " " +
skillet.ingredient + " & " +
amountOfGrease + " of Grease" );
console.log( isHot ? "Hot" : "Cold" );
};
}( window.skillet = window.skillet || {}, jQuery ));

try {
//12 Bacon Strips & 1 Cup of Grease
skillet.toString(); //Throws Exception
} catch( e ) {
console.log( e.message ); //isHot is not defined
}

You can execute and modify the above code from jsFiddle.

First, since we have a Self-Executing Anonymous Function, we can actually provide some parameters to pass to it when it executes. In this case we are passing 2 arguments to the anonymous function.

The first argument looks quite strange. What is window.skillet = window.skillet || {} doing? The code checks to see if skillet exists in the global namespace (window). If it does not exist, then window.skillet is assigned an empty object literal. Using this approach we can build a library across JavaScript files. If another script uses the same technique, then it will pass in the existing instance and append logic to it. Inside the Anonymous Function, if we want something to be public, then we append it to the skillet object. Any other properties or methods will be considered private.

The second argument passed in jQuery. The benefit of this is that the named parameter is referenced as $, which allows us to refer to jQuery as $ within the Anonymous Function without having to worry that it will conflict with the $ declared in other JavaScript libraries. This is a common practice that you will most likely run across when looking at well written jQuery code.

You might notice a 3rd parameter to the Anonymous Function called undefined. Why did we add that parameter and why didn’t we pass an argument to it? In JavaScript, you can unfortunately redefine what undefined means. Imagine that some code somewhere deep in one of your 3rd party libraries redefines undefined to something strange like true. If anywhere in your code you test against undefined, then you code will most likely not behave like you intended. In JavaScript, if you have a parameter that doesn’t have a matching argument, then it’s value is set as undefined. By using this trick, it can save us from the bad code someone wrote to redefine undefined.

Pros
•Gives you the ability to have public and private properties and methods
•The code inside doesn’t use the Object Literal notation
•Keeps undefined’s value as undefined in case someone overrode the value
•Allows you to use $ inside your code without worrying about clashing with other libraries
•Allows your library to grow across files using the “window.namespace = window.namespace || {}” technique
•A common pattern that you will see in many libraries, widgets, and plugins

Cons
•Slightly more complicated pattern, but not overly difficult to understand
If you are interested in digging deeper into some of the patterns I mentioned above then I encourage you to check out Klaus Komenda’s post entitled JavaScript Programming Patterns. The article provides an insightful view of how JavaScript patterns have changed over the years.

Best Practice
In JavaScript it is very important to declare all of your variables and it is considered a best practice to limit the number of objects in the global namespace. Depending on the situation you are developing you may need one or more of the concepts listed above to organize your code and keep it from colliding with other libraries. There is no pattern that is a Silver Bullet, but rather you should assess where you are at and examine the pros and cons of each pattern to address your situation.

2. Not Declaring Arrays & Objects Correctly
JavaScript is a prototypal language and not a classical language like C#. Although JavaScript does have a new operator which looks a lot like C#, you should not use it for creating new objects or arrays.

What Not To Do
You might be tempted to use the new keyword to create your objects in JavaScript.

//Bad way to declare objects and arrays
var person = new Object(),
keys = new Array();

The new keyword was added to the language partially to appease classical languages, but in reality it tends to confuse developers more than help them. Instead, there are native ways in JavaScript to declare objects and arrays and you should use those instead.

Preferred Way
Instead of using the previous syntax, you should instead declare your objects and arrays using their literal notation.

//Preferred way to declare objects and arrays
var person = {},
keys = [];

Using this pattern you actually have a lot of expressive power using Object Literals and array initializer syntax. As we mentioned earlier, you should be careful to know that Object Literals are not JSON.
//Preferred way to declare complex objects and arrays
var person = {
firstName: "Elijah",
lastName: "Manor",
sayFullName: function() {
console.log( this.firstName + " " +
this.lastName );
}
},
keys = ["123", "676", "242", "4e3"];


Best Practice
You should declare all of your variables using their literal notation instead of using the new operation. In a similar fashion you shouldn’t use the new keyword for Boolean, Number, String, or Function. All they do is add additional bloat and slow things down.

The only real reason why you would use the new keyword is if you are creating a new object and you want it to use a constructor that you defined. For more information on this topic you can check out Douglas Crockford's post entitled JavaScript, We Hardly new Ya.

Conclusion
This article has covered some of the common issues you might address coming from C# to JavaScript. I have committed just about everyone of the above Bad Practices myself when I starting to code in JavaScript heavily.

With the recent serge of jQuery hitting the market and other great JavaScript libraries, it is important that we as developers understand the language instead of just thinking that our previous language knowledge will get us by.

This article only covers some of the things you should know. The next post in the series will be published next week. In the meantime please feel free to add comments of any other gotchas that you’ve experience coming from a C# background. I would love to hear about it. Sphere: Related Content

No hay comentarios: