JavaScript and popular programming paradigms (OOP, AOP, FP, DSL). Overview of the language to see what tools we can leverage to reduce complexity of our projects.
This part goes over language features and looks at OOP and AOP with JavaScript.
The presentation was delivered at ClubAJAX on 2/2/2010.
Blog post: http://lazutkin.com/blog/2010/feb/5/exciting-js-1/
Continued in Part II: http://www.slideshare.net/elazutkin/exciting-javascript-part-ii
Powerful Google developer tools for immediate impact! (2023-24 C)
Â
Exciting JavaScript - Part I
1. Exciting JavaScript
What makes it unique, how to use it.
Eugene Lazutkin
Dallas, TX, ClubAjax on 2/2/2010
1
2. Disclaimer
⢠I will use JavaScript, JS, and ECMAScript
interchangeably.
⢠The material is mostly based on
ECMAScript 3.
2
3. Why JavaScript?
⢠Browsers.
⢠CommonJS (ex-ServerJS) is gaining steam.
⢠CouchDB uses it to deďŹne âviewsâ.
⢠Node.js with its asynchronous event-based
I/O is red hot.
⢠An explosion of JavaScript run-time
environments and libraries.
3
4. JS the Language I
⢠JavaScript packages a formidable set of
tools we can leverage to reduce
complexity.
⢠While it doesnât provide a direct support
for some programming paradigms, it has
proper tools for us to do it.
4
5. JS the Language II
⢠Following paradigms can be supported:
⢠Object-oriented programming (OOP).
⢠Functional programming (FP).
⢠Aspect-oriented programming (AOP).
⢠Event-driven programming (EDP).
⢠Much more.
5
6. JS the Language III
⢠Letâs do an inventory of the language
facilities:
⢠To understand how JavaScript is different
from other languages.
⢠To understand what we can use to
improve meta-programming techniques.
6
8. 1 st class functions
⢠We can create them on the ďŹy.
⢠Anonymous function.
⢠We can write them inline.
⢠Function literals.
8
9. Function examples I
// ânormalâ function
function double1(x){
return 2 * x;
}
// anonymous function
var double2 = function(x){
return 2 * x;
};
9
10. Function examples II
// takes function as an argument
function scale(f, x){
return f(x);
}
// returns a function function
var makeDouble = function(){
return function(x){ return 2 * x; };
};
// combine them
var result = scale(makeDouble, 5);
10
11. Closures I
⢠Closures are:
⢠The most mysterious part of JavaScript.
⢠The bane of beginners, and the source of
most their errors.
⢠The cornerstone of Functional
Programming in JavaScript.
11
12. Closures II
⢠It is a function, which uses variables from its
lexical environment.
⢠JavaScript closure uses external variables by
reference.
⢠It can consume, and modify external
objects by name.
12
13. Closures: examples
var a = 1;
// available here: a
function f1(x){
var b = 2;
// available here: a, f1, b, x
function f2(y){
var c = 3;
// available here: a, f1, b, x, f2, c, y
return a + b + c + x + y;
}
}
13
14. var: ďŹne points I
⢠Only functions produce lexical scopes.
⢠It doesnât matter where var statement is
encountered: a variable is available
everywhere in the function body.
⢠Initialization is performed at the point of
declaration.
14
15. var: ďŹne points II
function f(x){
var n = 5, acc = 0;
// available here: f, x, n, acc, i, d, b
for(var i = 0; i < n; ++i){
var d = 2 * i;
acc += d;
}
// b is available here yet undefined
var b = 1000;
return t + b * x;
}
15
16. Closures vs. Pure
⢠Pure functions:
⢠Stateless.
⢠Have no side effects.
⢠Operate only on their arguments.
⢠Always return the same value for the
same arguments.
16
17. Why pure?
⢠Theoretically:
⢠Easier to analyze the program for
correctness.
⢠Code can be rearranged and optimized
by compilers.
⢠Opens up other techniques like lazy
evaluations.
17
18. Practical closures I
// generate unique DOM ID
var uniqId = (function(){
// truly private variables
var prefix = âuniq_â, n = 0;
return function(){
do{
var name = prefix + (n++);
}while(document.getElementById(name));
return name;
};
})();
18
19. Practical closures II
// parameterize a function
var scale = function(factor){
return function(x){
return factor * x;
};
};
// use
var double = scale(2), triple = scale(3);
var x = double(21);
var y = triple(14);
19
20. this: ďŹne points I
⢠Every function is called in a context of an
object or the global scope.
⢠this is a pseudo-variable, which always
points to the current context.
⢠It cannot be pulled from a closure.
⢠Contexts are not inherited.
20
21. this: ďŹne points II
⢠Specifying a context implicitly:
var name = âglobalâ;
var abc = function(a, b, c){ alert(this.name); };
var myObj = {name: âmyObjâ, method: abc};
// calling method in the context of myObj
myObj.method(1, 2, 3); // this === myObj
myObj[âmethodâ](1, 2, 3); // this === myObj
// calling function in the global context
abc(1, 2, 3); // this === global
21
22. this: ďŹne points III
⢠Specifying a context explicitly:
var abc = function(a, b, c){ alert(this.name); };
var myObj = {name: âmyObjâ};
// calling method in the context of myObj
abc.apply(myObj, [1, 2, 3]); // this === myObj
abc.call(myObj, 1, 2, 3); // this === myObj
22
23. this: ďŹne points IV
⢠Accessing outer this â just reassign this
to a regular variable:
function f1(){
// this is âthis #1â
var self = this;
function f2(){
// this is âthis #2â
// generally âthis #2â !== âthis #1â
// but âselfâ is guaranteed to be âthis #1â
}
23
24. arguments
⢠Another pseudo-variable with ďŹne points
and restrictions similar to this.
⢠It has two cool properties:
⢠âarguments.callerâ to specify who calls
you function.
⢠âarguments.calleeâ to specify your
function anonymously.
24
25. Hash-based objects I
⢠Everything is an object. Sounds familiar?
⢠All objects are hash-based dictionaries of
key-value pairs.
⢠Hash-based the access is constant.
⢠Embedded sub-objects and references can
produce arbitrary complex graphs.
25
26. Hash-based objects II
⢠We can add/remove custom properties at
will.
⢠Read-only objects: numbers, strings,
booleans, null, undefined.
⢠Objects are represented by references.
⢠Read-only objects simulate the âvalueâ
semantics.
26
28. OOP: short intro
⢠Object-oriented programming is an
important paradigm.
⢠It is a way to organize/partition code into
simple objects, which encapsulate state, and
associated functionality.
⢠There are many ďŹavors of OOP.
⢠The simplest ones are the most useful.
28
29. OOP: duck-typing I
⢠We can do OOP without classes!
⢠Example:
⢠We have an iterator interface:
⢠next() â returns ânextâ object.
⢠hasNext() â returns true, if there is
ânextâ object, and false otherwise.
29
30. OOP: duck-typing II
⢠Notable:
⢠The iterator interface here is a concept,
it doesât have any corresponding code.
⢠We donât derive anything from anything.
⢠We donât even need a class.
⢠We rely on human conventions rather
than syntactic/semantic contracts.
30
31. OOP: duck-typing III
⢠Letâs implement a counter from 1 to 5:
var it = {
value: 1, limit: 5,
hasNext: function(){ return this.value <= this.limit; },
next: function(){ return this.value++; }
};
// consumer
function consume(it){
while(it.hasNext()){ alert(it.next()); }
}
31
32. OOP: duck-typing IV
⢠Letâs implement a stack-based iterator:
var it = {
stack: [1, 2, 3, 4, 5],
hasNext: function(){ return this.stack.length; },
next: function(){ return this.stack.pop(); }
};
// consumer is exactly the same
function consume(it){
while(it.hasNext()){ alert(it.next()); }
}
32
33. No constructors?
⢠What if we want to create several similar
objects?
⢠We need constructors.
⢠Yes, it is possible to have constructors
without classes.
33
34. OOP: factories I
⢠Any function that return an object will do.
⢠Letâs construct our simple iterator:
function makeIt(from, to){
return {
value: from, limit: to,
hasNext: function(){ return this.value <= this.limit; },
next: function(){ return this.value++; }
};
}
34
35. OOP: factories II
⢠Closure version of our simple iterator:
function makeIt(from, to){
var counter = from;
function hasNext(){ return counter <= to; }
function next(){ return counter++; }
return {
hasNext: hasNext,
next: next
};
}
35
36. OOP: prototypes
⢠JavaScript has notions of constructors and
inheritance.
⢠But it doesnât provide classes per se.
⢠The inheritance is of prototypal nature:
⢠Objects can delegate (statically) some
properties to another object, and so on.
36
37. OOP: constructor I
⢠Constructor can be any function.
⢠When it runs its context is set to a newly
created object.
⢠Usually it is modiďŹed as a part of
initialization.
⢠Alternatively a constructor can return a
totally different object (rarely used).
37
38. OOP: constructor II
⢠Constructor has one important property:
prototype.
⢠It points to a delegatee object.
⢠Usually a constructor is invoked with the
new operator.
38
39. OOP: new operator
⢠new creates a proper object (a generic
object for user-deďŹned constructors).
⢠It takes a prototype from a constructor
object and assigns it to the newly created
object (this step cannot be done in any
other way).
⢠It passes the object as a context to a
constructor.
39
40. OOP: delegate
⢠Now we can write a generic delegate:
var delegate = (function(){
function temp(){} // itâll hold a prototype reference
return function(obj){
temp.prototype = obj; // saving a prototype
var t = new temp(); // new object is delegated
temp.prototype = null; // avoid a memory leak
return t;
};
})();
40
41. OOP: using delegate I
var a = {
hello: function(){ console.log(âHello!â); },
zen: function(){ console.log(âO-O-OMâ); }
};
a.hello(); // Hello!
var b = delegate(a);
b.hello(); // Hello!
b.hello = function(){ console.log(âHuh?â); };
a.hello(); // Hello!
b.hello(); // Huh?
41
42. OOP: using delegate II
var c = delegate(b);
a.hello(); // Hello!
b.hello(); // Huh?
c.hello(); // Huh?
c.hello = function(){ console.log(âHI!!!â); };
a.hello(); // Hello!
b.hello(); // Huh?
c.hello(); // HI!!!
delete c.hello;
c.hello(); // Huh?
42
43. OOP: advanced I
⢠We just saw how we can organize our code
using the single inheritance.
⢠Many JS libraries provide OOP helpers:
⢠An analog of delegate().
⢠A mix in procedure, which adds
properties of one object to another.
43
44. OOP: advanced II
⢠dojo.declare() provides:
⢠Multiple inheritance based on class
linearization (using C3 MRO).
⢠Chaining for constructors and regular
methods.
⢠Simple inherited() calls to augment an
existing functionality.
44
47. AOP: short intro I
⢠Aspect-oriented programming pays
attention to cross-cutting concerns
implemented as advices, which can be
applied to pointcuts.
⢠Pointcuts deďŹne âeventsâ in our program
where we can insert some extra code.
47
48. AOP: short intro II
⢠Examples of cross-cutting concerns:
⢠Time/proďŹle a function call.
⢠Put all newly constructed objects in a list
so we can iterate over them, remove
them on destruction.
⢠Cache results of an expensive pure
function, and reuse them.
48
49. AOP: short intro III
⢠All examples demonstrate common things:
⢠Code required to perform an advice
does not depend on a nature of
functions/objects it operates on.
⢠AOP is just another way to structure you
code.
49
50. AOP in JavaScript
⢠We can reliably intercept one thing: a
method call.
⢠In most cases it is more than enough for
practical programmers.
⢠All advice types can be implemented:
⢠before, around, after, afterReturning,
afterThrowing.
50
51. AOP: toy example I
⢠Letâs implement the âafterReturningâ
advice:
function attachAfterReturning(obj, name, advice){
var old = obj[name];
obj[name] = function(){
var result = old.apply(this, arguments);
advice.call(this, result);
return result;
};
}
51
52. AOP: toy example II
⢠Letâs implement the âbeforeâ advice:
function attachBefore(obj, name, advice){
var old = obj[name];
obj[name] = function(){
advice. apply(this, arguments);
return old.apply(this, arguments);
};
}
52
53. AOP: toy example III
⢠Letâs implement the âaroundâ advice:
function attachAround(obj, name, advice){
var old = obj[name];
obj[name] = function(){
return advice.call(this, arguments, old);
};
}
53
54. AOP: advice example I
⢠Simple tracer as âaroundâ advice:
var tracer = (function(){
var stack = [];
return function(args, old){
console.log(â=> startâ);
stack.push(new Date().getTime());
var result = old.apply(this, args);
var ms = new Date().getTime() - stack.pop();
console.log(â<= finish: â + ms);
return result;
};
})();
54
55. AOP: using tracer
var fact = {
fact: function(n){ return n < 2 ? 1 : n * this.fact(n); }
};
attachAround(fact, âfactâ, tracer);
fact.fact(3);
// prints:
// => start
// => start
// => start
// <= finish: XX
// <= finish: YY
// <= finish: ZZ
55
56. AOP: advice example II
⢠Simple 1-arg memoization as two advices:
var memoizer = function(){
var cache = {};
var memoizer = function(args, old){
var arg = args[0];
if(arg in cache){ return cache[arg]; }
return cache[arg] = old.call(this, arg);
};
memoizer.clear = function(){ cache = {}; };
return memoizer;
};
56
57. AOP: using memoizer
var fact = {
fact: function(n){ return n < 2 ? 1 : n * this.fact(n); }
};
var mem = memoizer();
attachAround(fact, âfactâ, mem);
console.log(fact.fact(55));
// repeated calculations will be very fast:
console.log(fact.fact(60));
// âforgetâ previously calculated values
mem.clear();
57
58. AOP and OOP I
⢠There are some parallels between AOP and
OOP:
⢠Method chaining can be imagined as
advices:
⢠Constructors are chained using âafterâ
advices.
⢠Destructors are chained using âbeforeâ.
58
59. AOP and OOP II
⢠Super calls (inherited() calls) can be
represented as âaroundâ advice.
⢠These similarities are not by chance. They
reďŹect fundamental properties of OOP
expressed in AOP terms.
59
60. More on AOP
⢠I wrote a blog post about AOP in JS and
how it is implemented in Dojo:
⢠http://lazutkin.com/blog/2008/may/18/
aop-aspect-javascript-dojo/
60
61. Intermediate stop
⢠âŚand I ran out of time.
⢠Next month we will talk about:
⢠Functional programming (FP).
⢠Domain-speciďŹc languages (DSL).
⢠How JavaScript supports code generation
(CG) techniques.
61
62. About me
⢠I am an independent software developer.
⢠My web site:
⢠http://lazutkin.com
⢠Follow me on Tweeter:
⢠http://twitter.com/uhop
62