Javascript
“The World's Most Misunderstood Programming Language (Has Become the World's Most Popular Programming Language)”
-- Douglas Crockford --
http://javascript.crockford.com/
Despite its popularity, few know that JavaScript is a very nice dynamic object-oriented general-purpose programming language.
You can do anything with it!! (cloud9, this presentation!!)
Presentation Goal
- Explain some peculiar concepts about JavaScript, not found in any classical OO language ...
-
... so that you will be able to understand what this code does and why it works
/* The .bind method from Prototype.js */
Function.prototype.bind = function(){
var fn = this,
args = Array.prototype.slice.call(arguments),
object = args.shift();
return function(){
return fn.apply(object,
args.concat(Array.prototype.slice.call(arguments)));
};
};
At the end of this presentation, you should be able to go out there, pick the source code of a javascript framework (jQuery, Kendo, ...), read it and understand why it works (and hopefully learn many more interesting things!!)
function()
- functional language, functions are first class citizens
- note the semicolon (;) at the end of the expression
function.call()
-
directly using parentheses
diameter(2);
-
using Function object methods
diameter.call(null, 2);
diameter.apply(null, [2]);
-
The arguments object
function test() {
console.log(arguments);
}
test();
test("a");
test("a", 5);
JavaScript performs no check on the number of arguments. Any formal parameter that does not receive an actual value will have undefined as its value.
scope
Indeed, a variable is defined even BEFORE its "var" definition, show a demo. We'll see why
We'll see what global scope means in a moment
Javascript execution
- a function's execution context delimits a function's scope and includes everything that's inside curly brackets ({}) in a function's definition (it's defined statically, based on how the code is written)
- An execution context has a variable object. Any variables and functions defined inside the function are added as properties on this object
- Before running any code, the JavaScript engine creates a global object whose initial properties are the built-ins defined by ECMAScript, such as Object, String, Array and others, in addition to host defined properties
- all these are NOT objects usable by your program, they're used by the javascript runtime to execute the codeJavascript execution
For any formal parameters, add corresponding properties on the variable object and let their values be the values passed as arguments to the function.
For any function declarations, add corresponding properties on the variable object whose values are the functions. If a function declaration uses the same identifier as one of the formal parameters, the property is overwritten.
For any variable declarations, add corresponding properties on the variable object and initialize the properties to undefined, regardless of how the variables are initialized in source code. If a variable uses the same identifier as an already defined property (i.e., a parameter or function), do not overwrite it.
Now you undestand the behaviour seen previously.
Best practice to avoid problems: declare all variables at the start of a scope (i.e. a function)
Scope Chain
-
Whenever a function is called, control enters a new execution context.
-
The activation object is used for identifier resolution inside the function. In fact, identifier resolution occurs through the scope chain, which starts with the activation object of the current execution context. At the end of the scope chain is the global object.
function adder(base) {
return function(num) {
return base + num;
};
}
var inc = adder(1);
console.log(inc(5));
Javascript objects (intro)
Container of key-value pairs
var o1 = {
name: “circle”,
radius: 2,
diameter: function () { /* ... */ }
};
console.log(o1.name);
var o2 = {
“invalid-key-constant”: “value”
};
console.log(o2[“invalid-key-constant”]);
The this keyword
this is not caused by the fact that the "test" function is declared in the same scope of the "a" variable; show an example with a function defined inside "test" function
The this keyword
-
calling it as a method causes this to be the object through which the function is called
var person = {
name: "World",
sayHi: function() {
return "Hello " + this.name;
}
};
person.sayHi();
-
allows JavaScript objects to share function objects and still have them execute on the right object
var person2 = {
name: "Gabriele",
sayHi: person.sayHi
};
person2.sayHi();
The this keyword
person.sayHi.call({ name: "John" });
Closure
function adder(base) {
return function(num) {
return base + num;
};
}
- inside the anonymous function returned by adder, the base variable is known as a free variable
- it lives on after the adder function has finished executing, as long as someone holds a reference to the returned function
JavaScript supports nested functions, which allows for closures that can keep private state, and can be used to implement many useful patterns
Stateful Functions
- A closure can maintain state through its free variables.
- The scope chain that allows access to these free variables is only accessible from within the scope chain itself, which means that free variables by definition are private.
function uniqueIdGenerator(start) {
var currentId = start || 0;
return function() {
return currentId++;
}
}
Every new execution of "uniqueIdGenerator" creates a new execution context and a new scope chain; this means that a generator's state cannot interfere with the state of a previously created generator
Immediately Called Anonymous Functions
(function () {
/* ... */
}());
-
avoid littering the global scope with temporary variables by simply wrapping our code in a self-executing closure
- simulate block scope
var names = ['John', 'Gabriele'];
(function() {
var i;
for (i = 0; i < names.length; i++) {
console.log("Hello " + names[i]);
}
}());
console.log(i);
JavaScript only has global scope and function scope, we need to avoid leaking objects into the global scope because doing so increases our chances of naming collisions with other scripts (and bugs in our scripts)
This pattern is used EVERYWHERE, see some jQuery plugin (i.e. cycle jquery plugin)
Namespaces
A good strategy to stay out of the global scope is to use some kind of namespacing
var reply = {
applicationConfig: {
config1: "...",
config2: 42
}
customCalendar: function() { /* ... */ }
};
You could create your personal framework and it should have a single entry point stored inside the global object.
This pattern too is used EVERYWHERE (jQuery, Kendo, ...)
Resources and Credits
(in random order)