Advanced Javascript – EDGE Academy 2013 – First Class Functions



Advanced Javascript – EDGE Academy 2013 – First Class Functions

0 0


advancedjs

Advanced Javascript

On Github johanbjurling / advancedjs

Advanced Javascript

EDGE Academy 2013

Johan Bjurling / Fredrik Wärnsberg

Fredrik Wärnsberg

Johan Bjurling

netlight.js

reveal.js

var h = document.getElementsByTagName("head")[0];
var l = document.createElement("link");
l.href = "css/theme/netlight.css";
l.rel = "stylesheet";
Run it!

Overview

First Class Functions Closures This Asynchronous programming Prototype based inheritance Tools, frameworks, projects Oh hey, these are some notes. They'll be hidden in your presentation, but you can see them if you open the speaker notes window (hit 's' on your keyboard).

First Class Functions

In Javascript, functions are objects and can thus be treated just like objects

function myFunction() {
  alert("myFunction");
}

alert(myFunction instanceof Object);
Run it!

They can be stored in variables

var f = function myFunction() {
  alert("myFunction");
}
f();
Run it!

They can be sent as arguments to other functions

function myFunction(f) {
  f();
}
Run it!

They can be returned from functions

function myFunction() {
  return function() {
    alert("johan");							
  };
}
Run it!

They can have properties

function myFunction() {
  alert("alert!");							
}
myFunction.myProperty = "something";
Run it!

They can be properties

var myObject = {
  myFunctionAsProperty: function() { alert("yeah"); }
};
Run it!

They can be extended

Function.prototype.curry = function() {
  var fn = this, slice = [].slice, args = slice.call(arguments);
  return function() {
    return fn.apply(this, args.concat(
	  slice.call(arguments)));
  };
};
Run it!

Closures

A simple example

function makeClosure() {
  var name = "Johan";
  return function() {
    alert(name);
  };
}
Run it!

A somewhat more advanced example

function makeAdder(x) {
  return function(y) {
    return x + y;
  };
}
Run it!

A practical example

Emulate privacy (Module Pattern)

The Module Pattern

var myModule = (function() {
  var privateCounter = 0;
  function changeBy(val) {
    privateCounter += val;
  }
  return {
    increment: function() {
      changeBy(1);
    },
    decrement: function() {
      changeBy(-1);
    },
    value: function() {
      return privateCounter;
    }
  };
})();
					
Run it!

A pitfall

Creating closures inside loops

Closures in loops

var arrayOfFunctions = [];
for (var i = 0; i < 3; i++) {
  arrayOfFunctions[i] = function() { alert(i); };
}
						
Run it!

How 'this' works in Javascript

You always have 'this' set to something

 

alert(this); // [object Window]

 

window.name = "A name";
var test = function(){
  alert(this);
};
test();
Run it!

 

$('a').on('click', function(){
  alert(this); // HTMLElement
});

 

var object = {
  name: 'A Name',
  greet: function(greeting){
    alert(greeting + this.name);
  }
};
object.greet('Hello, ');
Run it!
window.name = "window's name";
var object = {
  name: 'An object',
  init: function(){
    var self = this;
    var test = function(){
      alert(this.name);
      alert(self.name);
    }
    test();
  }
};

object.init();
Run it!
 var object = {
  name: "A name",
  test: function(greeting){ alert(greeting + this.name); }
}

var stolen = object.test;
stolen("Hello, ");
Run it!

Controlling this

 

Function.prototype.call(context, arg1, arg2, ...argN);
Function.prototype.apply(context, [args]); 
stolen.call(obj, "Bonjour, ");

stolen.apply(obj, ["Guten tag, "]);

Remembering this

 

Function.prototype.bind = function(context){
  var __func = this,
      slice  = [].slice,
      args   = slice.call(arguments, 1);
  return function(){
    args = args.concat(slice.call(arguments, 0));
    __func.apply(context, args);
  };
}; 
 stolen.bind(obj).greet('Hello, ');

Asynchronous programming

User me = TwitterUserSerivce.getUser("warnsberg");
List<User> followers = TwitterUserService.getFollowers(me);
List<Tweets> tweets = new ArrayList<Tweets>();
tweets.addAll(TwitterService.loadRecentTweets(followers));
System.out.println(Joiner.on(", ").join(tweets));

What would happen in Javascript

Javascript is single threaded

Event loop

When it needs to do something that won't return immediately, it is done asynchronously.

The basic idea

 

twitter.getUser(callbackFunction);
twitter.getUser({
  success: function(user){ console.log(user); }
});

Async error handling

 

twitter.getUser(function(error, user){ /* check if there was an error */ });
twitter.getUser({
  success: function(user){ /* ... */ },
  error: function(error){ /* ... */ }
});

Rembember our old example?

 

User me = TwitterUserSerivce.getUser("warnsberg");
List<User> followers = TwitterUserService.getFollowers(me);
List<Tweets> tweets = new ArrayList<Tweets>();
tweets.addAll(TwitterService.loadRecentTweets(followers));
System.out.println(Joiner.on(", ").join(tweets));

Hang tight, because this we're going to over lots of things quickly.

twitter.getUser("warnsberg", {
  success: function(user){
  },
  error: function(error){
    console.log('Couldn\'t load tweets :(');
  }
});
 twitter.getUser("warnsberg", {
  success: function(user){
    twitter.getFollowers(user, {
      success: function(followers){
      },
      error: function(error){
        console.log('Couldn\'t load tweets :(');
      }
    });
  },
  error: function(error){
    console.log('Couldn\'t load tweets :(');
  }
}); 
twitter.getUser("warnsberg", {
  success: function(user){
    twitter.getFollowers(user, {
      success: function(followers){
        twitter.loadRecentTweets(followers, {
            success: function(tweets){
              console.log(tweets.join(', '));
            },
            error: function(error){
              console.log('Couldn\'t load tweets :(');
            }
        });
      },
      error: function(error){
        console.log('Couldn\'t load tweets :(');
      }
    });
  },
  error: function(error){
    console.log('Couldn\'t load tweets :(');
  }
}); 

...but that's horrible!

Taming the async

Named functions saves the day!

var twitterError = function(error){
  console.log('Couldn\t load tweets :(');
};

var loadUser = function(){
  twitter.getUser('warnsberg', {
    success: loadFollowers,
    error: twitterError
  });
}
var loadFollowers = function(user){
  twitter.getFollowers(user, {
    success: loadTweets,
    error: twitterError
  });
}

var loadTweets = function(followers){
  twitter.loadRecentTweets(followers, {
    success: printTweets,
    error: twitterError
  });
};
var printTweets = function(tweets){
  console.log(tweets.join(', '));
}

Flow control

Composing small, re-usable parts.

Remember this?

 

asyncFunction(callback(err, result){});
 var twitterError = function(error){
  console.log('Couldn\t load tweets :(');
};

var loadUser = function(next){
  if(err) return next(err);
  twitter.getUser('warnsberg', next);
}

var loadFollowers = function(err, user, next){
  if(err) return next(err);
  twitter.getFollowers(user, next);
}

var loadTweets = function(err, followers, next){
  if(err) return next(err);
  twitter.loadRecentTweets(followers, next);
}); 

...and our code turns into

 

async.waterfall([loadUser, loadFollowers, loadTweets],
  function(err, tweets){
    if(err) return console.log('Twitter error :(');
    console.log(tweets.join(', '));
  }
);

Promises

Giving async operations an object representation.

Basic API

 

var promise = new Promise(); promise.resolve(value); promise.reject(error); promise.then(callback);

Promises are chainable

 

var promise = new Promise();

promise.then(function(value){
  /* ... */
}).then(function(value){
  /* ... */
}, function(error){
  console.log('Error: ', error);
});

And our example turns into this

twitter.getUser('warnsberg').then(function(user){
  return twitter.getFollowers(user);
}).then(function(followers){
  return twitter.loadRecentTweets(followers);
}).then(function(tweets){
  console.log(tweets.join(', '));
}, function(error){
  console.log('Couldn\'t load tweets :(');
});

...and in ES.Next

spawn(function*() {
  try {
    let user = yield twitter.getUser('warnsberg');
    let followers = yield twitter.getFollowers(user);
    let tweets = yield twitter.loadRecentTweets(followers);
    console.log(tweets.join(', '));
  } catch(e) {
    console.log('Couldn\'t load tweets :(');
  }
});

Prototype based inheritance

Classical inheritance

Prototype inheritance

You inherit from an actual object, not from a class (blueprint). The inheritance is reflected in the "Prototype Chain".

This means prototype based inheritance is in fact much more similar to real life.

How is an object's prototype determined?

function Sheep() {
}

Run it!

How can this be used for inheritance?

Let's make a Pig Sheep

Let's make a Pig Sheep

function Sheep() {
}
							
Sheep.prototype.eyeColor = "blue";
							
function PigSheep() {
}
							
						
Run it!

Tools & Frameworks

Useful tools

Test frameworks

Some final goodies

Summary

In Javascript...

...functions are First Class Citizens. ...functions remember their context in "Closures". ..."this" is a VERY special keyword. ...you need to structure your callbacks. ...inheritance is based on objects (prototypes) not classes. ...there are lots of great tools, frameworks and projects for you to discover.

THE END

 

johanbjurling.github.com/advancedjs