$ whoami
Programming languages:
Compiled, general purpose: Java; C/C++; Scala
Scripting: JavaScript; Python; etc…
Experimental: Go; Dart; CoffeeScript
Recent works (non-project, related to JS):
NodeJS based stuff involving custom C++ extensions
Closure Compiler / Closure Templates enhancements
Agenda
OO Inheritance patterns in JS, which one to use?
Exploiting functional paradigm support
Asynchronity in JS
(still) common pitfalls, how to avoid them?
Agenda is based on my observations during every day work, working with 3rd party
sources.
OO & Inheritance Patterns
Devs tend to trust various frameworks to construct classes with
inheritance.
Popular types (Crockford’s terms):
„Pseudoclassical”
„Power-constructor” or „Parasital”
Trends:
Avoiding new keyword
Emulating private members with closures
Assembling objects from prototypes „by hand”
Pseudo-Classical Pattern
Popular types (Crockfod’s terms):
„Pseudo-classical”
„Power-Constructor” or „Parasital”
Concerning trends:
„Power constructors” – avoiding new keyword
Emulating private members with closures
Assembling objects from prototypes „by hand”
Object.create Polyfill
if (!Object.create) {
Object.create = function (o) {
if (arguments.length > 1) {
throw new Error('properties not implemented');
}
function F() {}
F.prototype = o;
return new F();
};
}
Difference of Resulting Objects
Prototype chain (__proto__):
„Pseudo-Classical” „Power-constructor”
Rectangle Rectangle + Shape
Shape Object
Object
Shape
Object
• layered structure, head object • flat structure, every head
has only instance members object has all members
• Instantiated with new • new keyword is optional
keyword
Which One is Better?
Power-Constructor pattern is extremely popular. A lot of frameworks
using it, including jQuery.
Do we need privates? – not really
Performance?
Demo time! Let’s evaluate performance with a particle system.
Conclusion
Despite a little bit more verbose, might look not as self-contained than
other variants, Pseudo-Classical inheritance is the way to go.
Advantages:
Way batter performance, especially at large number of instances
Better IDE recognition
Better tooling (Closure-Compiler friendly)
Functional Paradigm: Higher Order Functions
Functions could be passed as arguments, and could be returned by other
functions as return values.
Functions are threated as first-class values.
This provides a flexible way to compose programs.
Example:
Take the sum of the integers between a and b:
// sum of integers between a and b
function sumInts(a, b) {
var sum = 0, i;
for (i = a; i <= b; i += 1) {
sum += i;
}
return sum;
}
Take the sum of the cubes of all integers between a and b:
function cube(n) {
return Math.pow(n, 3);
}
function sumCubes(a, b) {
var sum = 0, i;
for (i = a; i <= b; i += 1) {
sum += cube(i);
}
return sum;
}
Example (continued)
function factorial(n) {
var i, f = 1;
for (i = 2; i <= n; i += 1) {
f *= i;
}
return f;
}
function sumFactorials(a, b) {
var sum = 0, i;
for (i = a; i <= b; i += 1) {
sum += factorial(i);
}
return sum;
}
Advanced version using higher-order functions
A generic sum:
function sum(f, a, b) {
var sum = 0, i;
for (i = a; i <= b; i += 1) {
sum += f(i);
}
return sum;
}
Rewritten special cases:
function identity(n) { return n; }
sumInts = function (a, b) { return sum(identity, a, b); };
sumCubes = function (a, b) { return sum(cube, a, b); };
sumFactorials = function (a, b) { return sum(factorial, a, b); };
Taking functions as parameters, but still a lot of redundancy here…
More advanced version, removed redundancy
Let’s rewrite sum to return a function:
function sum(fn) {
return function (a, b) {
var sum = 0, i;
for (i = a; i <= b; i += 1) {
sum += fn(i);
}
return sum;
};
}
So specific cases could be written as:
sumInts = sum(identity);
sumCubes = sum(cube);
sumFactorials = sum(factorial);
Or could be invoked without the middle-man:
// equivalent to sumCubes
sum(cube)(3, 5);
// sum of the halves of integers between a and b
sum(function (n) { return Math.round(n / 2); })(3, 5);
Asynchronicity
JS is…
Event driven
Single threaded (setTimeout doesn’t do the trick)
Long running code could block execution, UI responsiveness
Standard APIs rely on call-backs
They are often nested multiple levels
Hard to debug: reading the call-stack is hard; exceptions
might not get propagated up to the initiator
„Pyramid of Doom”!
Difficulties with Call-backs
„Pyramid of doom”
When the code marches to the right faster than it marches forward:
step1(function (value1) {
step2(value1, function(value2) {
step3(value2, function(value3) {
step4(value3, function(value4) {
// Do something with value4
});
});
});
});
This is sequential, what if step3 could be executed when both step1 and
step2 are completed, but those two steps doesn’t depend on each other?
Difficulties with Call-backs (continued)
Synchronizing multiple call-backs
var callbackACompleted = false;
var callbackBCompleted = false;
function callbackA(result) {
// process result
callbackACompleted = true;
if (callbackBCompleted) {
done();
}
}
function callbackB(result) {
// process result
callbackBCompleted = true;
if (callbackACompleted) {
done();
}
}
function done() {
// things to do when boot callbacks completed their jobs
}
processA(params, callbackA);
processB(params, callbackB);
Deferred objects / Promises / Futures
Designed to solve exactly these problems
Under used, not widely known
API
done()
then() A promise is the stripped down
Promise version of the deferred instance,
fail()
Deferred doesn’t contain any state mutator
always() methods.
resolve()
reject()
Usage: a task creates a deferred object, and resolves (or fails it) later by
calling .resolve() / .reject(). Clients use promise, which doesn’t provide state
manipulation, just change subscription methods.
Pitfalls
Array
Deleting
delete arr[2]; // [1, 3, undefined × 1, 29, 45, 51]
arr.splice(2, 1); // ok! [1, 3, 29, 45, 51]
Never attempt to remove an element from an array with delete, use splice.
Sorting
arr = [1, 3, 24, 29, 45, 51];
arr.sort(); // [1, 24, 29, 3, 45, 51]
arr.sort(function (x, y) {return x > y;}); // ok
What happened? Don’t relay on default sort functionality; implement getComparator() ->
function for objects.
Pitfalls (continued)
Number Conversion
+"08"; // ok (8)
+new Date; // .valueOF()
Number("08"); // same as +
parseInt("08"); // 0 why?
parseInt("ff", 16); // 256
Prefer the + unary operator, you may implement valueOf(), always provide radix for
parseInt()!
Typeof
// typeof
typeof array === "object"; // Array.isArray() (polyfill)
typeof null === "object"; // spec error :(
Worst is typeof null, it returns „object” as a result of an early language specification.