On Github johanbjurling / advancedjs
Johan Bjurling / Fredrik Wärnsberg
var h = document.getElementsByTagName("head")[0]; var l = document.createElement("link"); l.href = "css/theme/netlight.css"; l.rel = "stylesheet";Run it!
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!
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!
Emulate privacy (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!
Creating closures inside loops
Closures in loops
var arrayOfFunctions = []; for (var i = 0; i < 3; i++) { arrayOfFunctions[i] = function() { alert(i); }; }Run it!
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!
Function.prototype.call(context, arg1, arg2, ...argN);
Function.prototype.apply(context, [args]);
stolen.call(obj, "Bonjour, "); stolen.apply(obj, ["Guten tag, "]);
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, ');
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));
When it needs to do something that won't return immediately, it is done asynchronously.
twitter.getUser(callbackFunction);
twitter.getUser({ success: function(user){ console.log(user); } });
twitter.getUser(function(error, user){ /* check if there was an error */ });
twitter.getUser({ success: function(user){ /* ... */ }, error: function(error){ /* ... */ } });
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 :('); } });
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(', ')); }
Composing small, re-usable parts.
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); });
async.waterfall([loadUser, loadFollowers, loadTweets], function(err, tweets){ if(err) return console.log('Twitter error :('); console.log(tweets.join(', ')); } );
Giving async operations an object representation.
var promise = new Promise(); promise.resolve(value); promise.reject(error); promise.then(callback);
var promise = new Promise(); promise.then(function(value){ /* ... */ }).then(function(value){ /* ... */ }, function(error){ console.log('Error: ', error); });
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 :('); });
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 :('); } });
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!
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.