For the past few days I’ve been teaching myself JavaScript for a PhD
project. I’m using the excellent book “JavaScript: The Good Parts” by
Douglas
Crockford .
To begin with, I took notes in my hand-written note book. But that was
slow and clunky. So I started making notes in Google Docs. But that
doesn’t have syntax highlighting. So it finally dawned on me: the best
place to make notes whilst learning a new language is in code! This
feels so blindingly obvious now that I feel dumb mentioning it but it
took me a little while to figure out. Of course, we all tinker with
code snippets whilst learning a new language. But I’m now trying to get
into the habit of creating a new file for each topic, and to put lots of
comments in the code to explain each new language feature that I learn.
The code will be my (runnable) notes.
For example, here’s my file on the topic of function invocation:
/*
From Chapter 4: Functions.
Section on "Invocation"
* Every function receives 2 additional parameters: 'this' and 'arguments'.
* There are no runtime errors if a function is called with the
wrong number of arguments.
* The value of 'this' is determined by the invocation pattern.
* There are 4 invocation patterns in JS:
* method invocation
* function invocation
* constructor invocation
* apply invocation pattern
*/
/****************************
* 1: METHOD INVOCATION
*
* When a function if stored as a property of an object,
* we call it a METHOD. When a method is invoked,
* 'this' is bound to that object.
*/
// Public method; can be used in any number of objects
function incrementFunc ( inc )
{
// 'this' will be bound to the object for whom
// this function is a method. Binding happens
// at invocation time.
this . value += typeof inc === ' number ' ? inc : 1 ;
}
function methodInvocationTest ()
{
print ( " Method invocation pattern: " );
// Create an object literal
var myObject = {
value : 0 ,
increment : incrementFunc // Very late binding. Bound at invocation time.
};
print ( " Initial value = " + myObject . value );
myObject . increment ();
print ( " After incrementing by default amount = " + myObject . value );
myObject . increment ( 40 );
print ( " After incrementing by 40 = " + myObject . value );
// 'incrementFunc' can be used in any number of objects, e.g.:
var myObject2 = {
value : 1000 ,
increment : incrementFunc
};
print ( " My object 2 " );
print ( " Initial value = " + myObject2 . value );
myObject2 . increment ();
print ( " After incrementing by default amount = " + myObject2 . value );
myObject2 . increment ( 40 );
print ( " After incrementing by 40 = " + myObject2 . value );
}
/******************************
* 2: FUNCTION INVOCATION *
******************************/
function add ( a , b ) { return a + b ; }
function functionInvocationTest ()
{
print ( " Function invocation test... " );
// Compare this definition of myObject
// to myObject in example above.
var myObject = {
value : 0 ,
increment : function () {
incrementFunc (); // WON'T WORK because the
// function's 'this' will be bound to the 'global' object,
// not to 'myObject'.
}
};
print ( " Initial value = " + myObject . value );
myObject . increment (); // WON'T WORK
print ( " After incrementing by default amount = " + myObject . value );
myObject . increment ( 40 );
print ( " After incrementing by 40 = " + myObject . value );
// When a function is not the property of an object,
// then it is invoked as a function:
var sum = add ( 3 , 4 );
// When a function is invoked like this, 'this'
// is bound to the 'global' object.
// This is a design mistake. Would've been better if 'this'
// of inner function was bound to the 'this' variable
// of the outer function. Hence an inner function to a method
// does not share the method's access to the object via 'this'.
myObject . double_broken = function () { // Broken version of the function
var helper = function () {
// Helper function's 'this' points to 'global' object,
// NOT to 'myObject'
this . value = add ( this . value , this . value );
}
helper (); // WON'T WORK!
};
print ( " Setting myObject.value = 1 " );
myObject . value = 1 ;
myObject . double_broken ();
print ( " After attemping to double, using broken function = " + myObject . value );
// work around. Takes advantage of 'closure'
// where inner function has access to out function's variables (EXCEPT 'this'):
myObject . double_works = function () { // Broken version of the function
var that = this ; // Create a local variable
var helper = function () {
// 'helper()' has access to 'that'
// because of JavaScript's 'closure'
that . value = add ( that . value , that . value );
}
helper ();
};
myObject . double_works ();
print ( " After attemping to double, using working function = " + myObject . value );
}
/******************************
* 3: CONSTRUCTOR INVOCATION *
******************************/
function constructorInvocationTest ()
{
// Constructor
var Quo = function ( string ) {
this . status = string ;
}
}
/******************************
* MAIN FUNCTION *
******************************/
window . onload = function ()
{
// methodInvocationTest();
functionInvocationTest ();
print ( " Done " );
}