Tuesday, December 14, 2010

JavaScript for PHP Developers

Hopefully, you've been a good geek in 2010. You've followed last year's advice and learned some JavaScript. Now, let's kick back with a glass of wine by the fireplace and take a moment to review what you've learned.

Objects

Most everything in JavaScript is an object, yet there are no classes. One way to define an object is by simply listing out the properties you want your object to have, separate them with commas, and wrap them in curly braces.
var calendar = {   name: "PHP Advent",   year: 2010 };
This simple object doesn't have any methods and looks more like an associative array in PHP. In fact, JavaScript doesn't have associative arrays and uses objects instead. To create a similar object in PHP, you can convert an array into an object:
$calendar = (object) array(   name => 'PHP Advent',   year => 2010 );
Simple, isn't it? No classes in sight. Objects are nothing more than fancy arrays. Fancy, because some of the array elements happen to be invokable, and we'll call them methods.
Another approach to creating objects is to start with an "empty" object.
var calendar = {};
This is just like $calendar = new stdClass(); in PHP. In JS, you can also do var calendar = new Object(); but that's just an exercise in typing.
You can add stuff (properties and methods) as you go:
calendar.name = "PHP Advent"; calendar.year = 2010; calendar.say = function () {   return this.name + ' ' + this.year; };
As you can see, JavaScript uses . instead of -> to access properties and methods.
calendar.say(); // returns "PHP Advent 2010"

I can has classes?

There's no such thing as classes in JavaScript, but if you really miss them, there are constructor functions instead.
function Calendar(year) {   this.name = "PHP Advent";   this.year = year;   this.say = function () {     return this.name + ' ' + this.year;   }; }
Constructor functions are just normal functions, but when called with new, they return objects.
var thisyear = new Calendar(2010),     lastyear = new Calendar(2009); thisyear.say(); // "PHP Advent 2010"
The equivalent in PHP would be something like:
class Calendar {   public $name = 'PHP Advent';   public $year;    function __construct($year) {     $this->year = $year;   }    function say() {     return $this->name . ' ' . $this->year;   } }  $thisyear = new Calendar(2010); $lastyear = new Calendar(2009);  echo $thisyear->say(); // "PHP Advent 2010"

Constructor functions versus normal functions

The way you define a constructor function in JavaScript is the same as with a normal function. The difference is in the invocation. When called with new, a function always returns an object. If you don't do anything special, the object referred to by this is returned.
alert(typeof new Calendar()); // "object"
When called without new, functions return whatever the return statement states in the body of the function. If there's no explicit return, the function will return the special value undefined.
alert(typeof Calendar()); // "undefined"
In order to distinguish the intent of a function when reading code, there's a widespread convention to name constructor functions with an uppercase first letter.
function MyConstructor() {} function myFunction() {}

Functions are objects

In JavaScript, where most things are objects, functions are objects, too. They can (and do) have properties and methods. You may also often see functions defined with var, like this:
var sum = function (a, b) {     return a + b; };
Now, check this out:
alert(sum.length); // 2
What's up with that‽ We accessed the property length of the object sum. Function objects have a length property which holds the number of parameters they expect (but don't enforce, as all parameters have a default value of undefined), in this case two: a and b.

Prototypes

Functions are objects, and they automatically get a property called prototype. This property is used when an object is created using a constructor function. The prototype is also an object (surprise, surprise).
function Calendar(year) {   this.year = year; }  Calendar.prototype.name = "PHP Advent";  Calendar.prototype.say = function () {   return this.name + ' ' + this.year; };  var thisyear = new Calendar(2010); thisyear.say(); // "PHP Advent 2010"
Here, we have a behavior that is much like the previous Calendar constructor we created. The object thisyear has access to say() as if it was its own method, but it comes transparently via the prototype.
Since the properties added to the prototype are shared and reused among all calendar objects, this is the place to put all reusable pieces of functionality. It's common practice to put all methods here.
Prototypes also provide ways to implement code reuse (inheritance).
You may notice that I said "empty" objects in quotes in the beginning of this article. I added them, because there's really no such thing as a blank object. All objects come with some pre-existing methods and properties, inherited down the prototype chain.

Anonymous functions

Often, you need one-off functions which don't need a name, or functions you intend to pass to other functions as callbacks. You can use anonymous functions in such cases.
In JavaScript, there's only a function scope — any variable defined outside a function is a global. This is obviously not desirable, because more globals means an increased risk of naming collisions.
A common pattern to reduce the number of temporary variables scattered around is to wrap self-contained pieces of code in an immediate function, meaning an anonymous function that is executed immediately.
// Alerts 3 and leaves no leftover variables. (function () {    var a = 1,       b = 2;    alert(a + b);  }());
In PHP, anonymous functions have been available since the release of version 5.3, and you can invoke them with call_user_func(), so the equivalent would be:
call_user_func(function() {   $a = 1;   $b = 2;   echo $a + $b; }); 
Or, in PHP before 5.3:
call_user_func(create_function('',   '$a = 1;    $b = 2;    echo $a + $b;' ));

Arrays

Arrays are simply lists of comma-delimited values wrapped in square brackets.
var dudes = ['Ilia', 'Andrei', 'Derick']; alert(dudes[2]); // "Derick"  // Or, if you want to type more: var dudes = new Array('Ilia', 'Andrei', 'Derick'); 
In PHP, this is similar:
$dudes = array('Ilia', 'Andrei', 'Derick'); echo $dudes[2]; // "Derick"
But, lo and behold, arrays are objects, too, so instead of passing the array to a function to get the array's size, you can access its length property.
// JS alert(dudes.length); // 3  // PHP echo count($dudes); // 3
Pushes, pops, and miscellaneous flips-flops also share similar behavior in PHP and JavaScript.
// JS dudes.pop(); // returns "Derick" dudes.toString(); // "Ilia,Andrei" dudes.push('Rasmus'); // returns 3, the new size dudes.toString(); // "Ilia,Andrei,Rasmus"  // PHP array_pop($dudes);  echo implode(',', $dudes); array_push($dudes, 'Rasmus'); echo implode(',', $dudes); 
The lazy and convenient PHP syntax to append to an array ($dudes[] = 'Ben';) is not available in JavaScript. You need to know the size or the array (to know the index of the last element), so you can use
 dudes[dudes.length] = 'Ben';  // dudes.push('Ben') is roughly equivalent to PHP's append operator. 
PHP's implode() and explode() become join() and split() in JavaScript.
// PHP $a = explode(',', '1,2,3'); echo implode(', then ', $a); // "1, then 2, then 3"  // JS var a = "1,2,3".split(','); alert(a.join(', then ')); // "1, then 2, then 3" 

Strings

There's nothing overly exciting about strings, other than — you guessed it — strings are objects, too. Well, technically there's a difference between a string object and a primitive string, but primitives are converted to objects when necessary, so there's no need to worry about the details.
// string object var so = new String('Crocodile'); alert(typeof s); // "object"  // primitive string var sp = 'Alligator'; alert(typeof s); // "string"  // But, primitives do behave like objects. alert("dude".length); // 4
As with arrays, all of your favorite string manipulation functions become a handful of methods.
// JS var s = "PHP Advent"; alert(s.substring(6)); // "vent" alert(s.replace(' ', 'eeee')); // "PHPeeeeAdvent" alert(s.replace(' ', 'eeee').substring(0, 7)); // "PHPeeee"  // PHP $s = 'PHP Advent'; print(substr($s, 6)); // "vent" print(str_replace(' ', 'eeee', $s)); // "PHPeeeeAdvent" print(substr(str_replace(' ', 'eeee', $s), 0, 7)); // "PHPeeee"

Callbacks

One last topic before we wrap it up for this year's article, callback functions. They illustrate two of the concepts we already talked about, functions and arrays.
A nice way to create reusable functions and methods is to have them accept other functions as parameters, which are known as callback functions.
A good example of this is in the sort() method of array objects in JavaScript. This method accepts a callback, much like usort() in PHP.
In JavaScript, if you want numeric sort, you need to provide a callback; there are no flags like in PHP's sort().
// JS var a = [2, 1, 30, 15]; a.sort(); // "1, 15, 2, 30", not what you expect  // PHP $a = array(2, 1, 30, 15); sort($a); // "1, 2, 15, 30", a.k.a., the right thing  // PHP like JS $a = array(2, 1, 30, 15); sort($a, SORT_STRING); // "1, 15, 2, 30"
Want numeric sort? Pass in a callback function:
// anonymous callback var a = [2, 1, 30, 15]; a.sort(function (a, b) {     return (a > b) ? 1 : -1; }); // "1, 2, 15, 30"  // Or, provide existing function as a callback. var a = [2, 1, 30, 15];  function numsort(a, b) {   return (a > b) ? 1 : -1; }  a.sort(numsort); // "1, 2, 15, 30"
If you were to do the same callback type of thing in PHP (not that you need to for numeric sorting, but it's an illustration), you can also provide an anonymous callback or an existing function:
// anonymous callback (PHP 5.3) $a = array(2, 1, 30, 15); usort($a, function ($a, $b) {   return ($a > $b) ? 1 : -1; });  // existing function as a callback (PHP 4+) $a = array(2, 1, 30, 15);  function numsort ($a, $b) {   return ($a > $b) ? 1 : -1; };  usort($a, 'numsort');

Wrapping up

JavaScript and PHP are very similar; they share common, C-like syntax, with curly braces, functions, ifs, for loops, &c. Yet, JavaScript has some specifics such as:
  • no classes
  • most things are objects, including arrays and strings
  • functions are objects
  • functions provide variable scoping
  • constructor functions
  • prototypes
Most often, when people say they hate JavaScript, they mean they hate the buggy, inconsistent, verbose DOM APIs provided by the browsers. Bare JavaScript (or ECMAScript) is a small, simple, and — some would say — beautiful little language, well worth learning. It doesn't have PHP's excellent manual with all the invaluable user-contributed notes (well, who does?) but there is Mozilla's reference for your bookmarking pleasure.
Here's to the happy hours JavaScripting in 2011!

No comments :

Post a Comment