Hey folks,
after the overwhelming interest in me writing a little bit about javascript on this blog, here comes my first post on that topic. It's actually the beginning of a little series (2 or 3 parts) that is going to be targeted at php developers who've only used JS by merging snippets/libraries together without really learning the language itself. For all of those posts please keep in mind that I'm mainly on the php site of town as well, but my recent trips to downtown javascript have hopefully taught me well ; ).
The JS developer toolkit
Coming from PHP you are probably spoiled with some good developer tools, especially Zend Studio or Ecplipse PHP. In javascript land there are not as many good editors out there. From the ones I've worked with I would recommend Notepad++. It's very light weight, supports numerous languages, can completely replace your windows notepad and I use it for all my JS (and CSS) needs right now. If you don't run the beautiful microsoft operating system (...) you might find Aptana interesting. It's based on eclipse and therefor runs on most major operating systems. It also has advanced support for code completion and some other gimmicks. Personally I don't use it because it's too memory hungry, especially when my php IDE is already opened. Other then that it's a really comfortable IDE to work with.
Now that you have an editor (no matter which one) you might want to start coding right away. But you shouldn't. Not because you couldn't, but because you still lack the most important JS (and web dev) tool that is available these days: Firebug. Without any question, it is the most advanced debugging tool out there. So please take some time to get to know it's features and learn how to use it, it will make your day.
The Basics
As all of you are already familiar with coding in php, I won't start at "Hello World", but I'll try to show some important differences in JS (from PHP) and point you to some resources for further reading. The first one would be gotAPI.com. If you see me using a function you are not familiar with, that's always a good place too look it up. (Side note: It also has support for the CakePHP/jQuery API).
Variables:
Just like PHP, javascript is a weakly typed language. This means you won't have to declare variable types (even so you can) and all variable types are converted to strings automatically when needed. The symbol used for gluing different strings together is '+' and not '.' like in php.
var myVariableA = 'Thinking';
var myVariableB = 'PHP'
// Logs: ThinkingPHP
console.log(myVariableA+myVariableB);
Oh and in case you wonder: 'console' is no default javascript object. It's only available when using firebug (or Firebug Lite). The usual JS way to get information is to use the alert() function. But the first time you produce and endless loop with an alert in it, you'll get to appreciate the console.log function offered by Firebug ; ). Another thing you just learned is that object functions/properties are accessed via '.' (as opposed to '->' in php), but more about this later.
Arrays
The good news is, javascript has decent support for indexed arrays. The bad news is, it's lacking associative arrays (that we php folks all know and love). But don't you worry, there is a workaround to fix this.
Working arrays in JS is very much like working with arrays in php. The only difference is that arrays (like everything else) in JS are actual objects not only variables containing literals. But that's a no-brainer, just have a look at the following examples:
// Note: var myArray = []; works just as well).
var myArray = new Array();
// Assign three values to the array.
myArray[0] = 'ThinkingPHP';
myArray[1] = 'suddenly';
myArray[2] = 'thinks';
// Now to show you we are working with an object:
console.log(myArray.length); // Will log: 3
// myArray[] = 'value'; does not work in javascipt, so we have to use Array.push
myArray.push('JavaScript');
// Will log: ThinkingPHP suddenly thinks JavaScript.
console.log(myArray.join(' ')+'.');
Now without really getting to the objects part yet, the Arrays are a good example to show another default behavior of the javascript language:
// This is notation to declare an array with one or more values (comma separated)
var varA = ['Thinking'];
var varB = varA;
// Push PHP at the end of array varB
varB.push('PHP');
// Display the results: ["Thinking", "PHP"]
console.log(varA);
In case the result of this little test surprises you, here is what happened: When executing 'var varB = varA;' you are not simply copying the value of variable 'varA', you are actually creating a reference to it. So if we would want to port this snippet to PHP, it would look like this:
$varA =
array('Thinking');
$varB = &
$varA;
// Push PHP at the end of array varB
array_push($varB,
'PHP');
// Prints: Array ( [0] => Thinking [1] => PHP )
print_r($varA);
So in case you actually wanted to create a copy of varA, this is how to do it in JS:
var varA = ['Thinking'];
// Clone the array
var varB = new Array();
for (var i=0; i<varA.length; i++)
{
varB[i] = varA[i];
}
varB.push('PHP');
// Logs: ["Thinking"]
console.log(varA);
// Logs ["Thinking", "PHP"]
console.log(varB);
Of course there are ways to do this in a more elegant way by creating a clone function, but let's keep it simple for now. Let's go back to what I said at the beginning of the array section: "Javscript does not support associative arrays". Yes, this is really a bummer at first. But the workaround is fairly simple, all it takes is to use objects instead:
Objects
One of the things that really have caused my to like javascript a lot recently is the great flexibility it offers. This really shows when working with objects the first time. In PHP you think of an object as an instance of a specific (fixed) class that basically consists of functions and properties. Well this is true for objects in JS as well. They have functions and properties. Where JS is different is the way you create an object. Unlike PHP where you define types of objects as classes, in javascript you often create objects on the fly. Another important point is that JS objects often do behave like associative arrays in php as well (but they are usually called hashes in JS).
Just consider the following example:
// same as var myJsObject= {};
var myJsObject = new Object();
// This looks like a typical associative array in PHP
myJsObject['varA'] = 'Thinking';
myJsObject['varB'] = 'PHP';
// But what's that? We are attaching a function to the object on the fly
myJsObject['ThinkingWhat'] = function()
{
// 'this' refers to the class this function resides in here. However this is not always the case in JS, more about this later.
console.log(this.varA, this.varB);
}
// Call the ThinkingWhat function
myJsObject['ThinkingWhat']();
// is the same as
myJsObject.ThinkingWhat();
Ok, I hope you are not too confused right now. You basically have to accept that in Javascript there is no difference between the object['propery'] and object.property notation. Another good example would be the little Array snippet we've already used:
var varA = ['Thinking'];
var varB = varA;
// Instead of varB.push('PHP') we can write:
varB['push']('PHP');
// Display the results: ["Thinking", "PHP"]
console.log(varA);
Now that you know this little JS secret, it's time to show you another one. The object we created on the fly a second ago, could have equally well been written like this:
var myJsObject =
{
varA: 'Thinking',
varB: 'PHP',
ThinkingWhat: function()
{
console.log(this.varA, this.varB);
}
};
The two syntaxes are equal to one another. One of the most popular usages of the second syntax right now is JSON. So if you didn't know what JSON was so far, it's simply sending javascript objects via AJAX and then eval'ing them on the client site so they can directly be accessed. However, please keep in mind that in JSON you always have to put quotes around object properties (!).
Functions
Alright, even if you've not done much JS coding so far, you've probably seen a simple JS function before:
// Logs: ThinkingPHP
console.log(ThinkingWhat('PHP'));
// A simple function with one argument
function ThinkingWhat(what)
{
// Return 'Thinking' plus the contents of the what argument
return 'Thinking'+what;
}
Now before I'll continue and reveal javascript's dark & nasty secret about functions (don't be scared, it's actually cool once you know how to deal with it), I want to show you a little bit more about passing arguments:
// Logs: ThinkingPHP
console.log(ThinkingWhat('PHP'));
// Logs: Thinkungundefined (and display no error)
// This means: Unlike in PHP, all function arguments in JS are optional on default (!)
console.log(ThinkingWhat());
// A simple function with one argument
function ThinkingWhat(what)
{
// Return 'Thinking' plus the contents of the what argument
return 'Thinking'+what;
}
// Logs: ThinkingPHP
console.log(FreeThinking('Thinking', 'PHP'));
function FreeThinking()
{
// In all functions the arguments object is available. It's an array
return arguments[0]+arguments[1];
}
Alright, this should be simple to understand. The two main lessons learned here are that in JS all function arguments are optional on default, and you always have access to an arguments object inside a function. You can think of it similiar to "$arguments = func_get_args()" in PHP. Now one thing you might miss is the ability to assign default values to parameters (like you do with optional parameters in PHP). Here is what to do:
// Logs: ThinkingPHP
console.log(ThinkingWhat());
// Logs: ThinkungJS
console.log(ThinkingWhat('JS'));
// A simple function with one argument
function ThinkingWhat(what)
{
// If what doesn't contain a value (that would evaluate to true) then assign 'PHP' to it
what = what || 'PHP';
// Return 'Thinking' plus the contents of the what argument
return 'Thinking'+what;
}
I really like the elegance this has. I know I often find myself writing if statements for checking empty variables and overwriting their contents with something else in PHP, so this is definitely a pretty cool feature of the JS language.
Ok, do you still remember me talking about the JS dirty little secret about functions? Well here it comes: The things what we just have looked at as 'functions' are actually a little bit more in JS. They are fully qualified class definitions (and btw. the only way to create them). This is probably the point were PHP developers feel odd and I agree that this takes a while to get used to. Nevertheless, check out this example to see how your innocent little function becomes a class:
// Defines a class named Blog
function Blog(url, author)
{
this.url = url;
this.author = author;
this.visit = function()
{
// Redirects the user to the url of this blog
window.location.href = this.url;
}
}
// Creates an instance of the Blog class called ThinkingPHP
var ThinkingPHP = new Blog('http://www.thinkingphp.org', 'Felix Geisendörfer');
// Logs: 'http://www.thinkingphp.org'
console.log(ThinkingPHP.url);
// Navigates to 'http://www.thinkingphp.org'
ThinkingPHP.visit();
Now as you can see the Blog function now doesn't just serve as a static function any longer. It has become the constructor and wrapper for the class Blog that contains two member variables (url and author) as well as one function (visit).
One of the most powerful JS features related to classes is the prototype property. It is a list of all members any given class has and can also be used to modify already created classes (and all instances of it). You've probably heard about the Prototype framework which makes extensive usage of this feature by adding new functions to already existing DOM element classes. Personally I don't use this feature very often and prefer to not modify existing objects (that's also one of the reasons I prefer jQuery over Prototype), but here is how to work with the prototype property regardless of that:
// Defines a class named Blog
function Blog(url, author)
{
this.url = url;
this.author = author;
}
// Creates an instance of the Blog class called ThinkingPHP
var ThinkingPHP = new Blog('http://www.thinkingphp.org', 'Felix Geisendörfer');
// This adds the function getAuthorFirstName to *all* Blog classes (also already created instances like ThinkingPHP)
Blog.prototype.getAuthorFirstName = function()
{
// Splits the author string at ' ' and returns the first value of that array
return this.author.split(' ')[0];
}
// Logs: 'Felix'
console.log(ThinkingPHP.getAuthorFirstName());
As you can see, even so we added the getAuthorFirstName function after we created the ThinkingPHP instance of the Blog class, it's still available in the instance of it. If you wonder of the practical usage of this: I find this feature pretty useful to modify 3rd party JS classes without actually touching the source of them. Because just like you can add class members using their prototype property, you can also overwrite existing ones.
This was part I
Alright, this was the first part of my 'A PHP developers guide to JavaScript' series. I hope you guys enjoyed it and I was able to communicate the most important aspects of JS in an easy-to-understand manner. I'm sorry it took me so long to publish this, but writing this required several hours so I had to split it up over several days. If you find errors, have questions or want to suggest anything I'd appreciate a comment ; ).
For Part II, I plan to cover things like scope, events, some JS coding techniques and will probably also start talking about jQuery a little bit. Meanwhile I'd recommend everybody to read Sergio Pereira's Quick guide to somewhat advanced JavaScript which I think has a lot of good information in it and is a little bit more detailed then my post on here.
Oh and before I forget: A Happy New Year to all of you! (Sorry I didn't care enough about Christmas to send out a greeting ^^). And also Happy Birthday to Daniel's cakebaker blog that just turned 1 year old!
-- Felix Geisendörfer aka the_undefined