1. Immediately Invoked Function Expressions (IIFEs)
The traditional function is declared but not executed, we need to explicitly execute it after definition. Like:
var foo = function() {
// ..
};
foo(); // execute
var x = function bar(){
// ..
};
x(); // execute
If we want to execute the function refrenced immediately before it. We can realize as following:
// `IIFE` function expression,
// then `()` executes it
(function IIFE(){ .. })();
2. Closure
You can think of closure as a way to “remember” and continue to access a function’s scope (its variables) even once the function has finished running.
For example:
function makeAdder(x) {
// parameter `x` is an inner variable
// inner function `add()` uses `x`, so
// it has a "closure" over it
function add(y) {
return y + x;
};
return add;
}
// makeAdder() will return a function, rather than a value. And the returned function can still access its parameter - x
// we can use it like this:
// `plusOne` gets a reference to the inner `add(..)`
// function with closure over the `x` parameter of
// the outer `makeAdder(..)`
var plusOne = makeAdder( 1 );
plusOne( 3 ); // 4 <-- 1 + 3
plusOne( 41 ); // 42 <-- 1 + 41
More on how this code works:
- When we call makeAdder(1), we get back a reference to its inner add(..) that remembers x as 1. We call this function reference plusOne(..).
- When we call plusOne(3), it adds 3 (its inner y) to the 1 (remembered by x), and we get 4 as the result.
The most common usage of closure in JavaScript is the module pattern. Modules let you define private implementation details (variables, functions) that are hidden from the outside world, as well as a public API that is accessible from the outside.
For example:
function User(){
var username, password;
function doLogin(user,pw) {
username = user;
password = pw;
// do the rest of the login work
}
var publicAPI = {
login: doLogin
};
return publicAPI;
}
// create a `User` module instance
var fred = User();
fred.login( "fred", "12Battery34!" );
NOTE: The User() function serves as an outer scope that holds the variables username and password, as well as the inner doLogin() function; these are all private inner details of this User module that cannot be accessed from the outside world.
The inner doLogin() function has a closure over username and password, meaning it will retain its access to them even after the User() function finishes running.
3. this Identifier
While it may often seem that this
is related to “object-oriented patterns,” in JS this
is a different mechanism.
If a function has a
this
reference inside it, thatthis
reference usually points to an object. But which object it points to depends on how the function was called.
function foo() {
console.log( this.bar );
}
var bar = "global";
var obj1 = {
bar: "obj1",
foo: foo
};
var obj2 = {
bar: "obj2"
};
// --------
foo(); // "global"
obj1.foo(); // "obj1"
foo.call( obj2 ); // "obj2" obj2.foo();
new foo(); // undefined
Note:
foo() ends up setting this to the global object in non-strict mode – in strict mode, this would be undefined and you’d get an error in accessing the bar property – so “global” is the value found for this.bar.
obj1.foo() sets this to the obj1 object.
- foo.call(obj2) sets this to the obj2 object.
- new foo() sets this to a brand new empty object.
4. Prototypes
When you reference a property on an object, if that property doesn’t exist, JavaScript will automatically use that object’s internal prototype reference to find another object to look for the property on. You could think of this almost as a fallback if the property is missing.
The internal prototype reference linkage from one object to its fallback happens at the time the object is created. The simplest way to illustrate it is with a built-in utility called Object.create(..)
. Example:
var foo = {
a: 42
};
// create `bar` and link it to `foo`
var bar = Object.create( foo );
bar.b = "hello world";
bar.b; // "hello world"
bar.a; // 42 <-- delegated to `foo`