-
When a function is invoked on or through an object, that object is the invocation context or
this
value for the function. -
Functions designed to initialize a newly created object are called constructors
-
JS functions can be nested within other functions and they have access to any variables that are in scope where they're defined. (closures)
-
In JS functions are first class objects because they can be passed as an argument to another function, returned from another function and also assigned as a value to a variable or property (methods)
-
Higher order functions are functions that accept a function, and/or return a function
// function declaration
function identifier(param1, param2, ...paramN) {}
function printprops(o) {
/**
* o = @type {object}
*/
for(var p in o) {
console.log(p + ': ' + o[p] + '\n');
}
}
function distance(x1, y1, x2, y2) {
/**
* x1 = @type {number}
* x2 = @type {number}
* y1 = @type {number}
* y2 = @type {number}
*/
var dx = x2 - x1;
var dy = y2 - y1;
return Math.sqrt(dx * dx + dy * dy);
}
function factorial(n) {
/**
* n = @type {number}
*/
if (n <= 1) return 1;
return n * factorial(n - 1);
}
// function expression
var square = function(n) {
/**
* n = @type {number}
*/
return n * n;
};
var f = function fact(n) {
/**
* n = @type {number}
*/
if (n <= 1) {
return 1;
}
return n * fact(n - 1);
};
data.sort(function(a, b){
/**
* a = @type {number || string}
* b = @type {number || string}
*/
return a - b;
});
var tensquared = (function(n) {
/**
* n = @type {number}
*/
return n * n;
}(10));
var fivesquared = (function(n) {
/**
* n = @type {number}
*/
return n * n;
})(5);
-
Variable declarations are hoisted, but assignments to those variables are not hoisted, so functions defined with expressions can't be invoked before they're defined.
-
Functions with no return value are sometimes called procedures
-
Functions can be invoked in four ways:
- as functions
()
- as methods
obj.method()
- as constructors
new FOO()
call()
andapply()
- as functions
-
A constructor invocation creates a new, empty object that inherits from the
prototype
property of the constructor. -
This newly created object is used as the invocation context, so the constructor function can refer to it with the
this
keyword -
call()
andapply()
allow you to explicitly specify thethis
value for the invocation, which means you can invoke any function as a method of any object, even if it is not actually a method of that object. -
Functions are executed using the scope chain that was in effect when they were defined.
-
Closures capture the local variable (and parameter) bindings of the outer function within which they are defined.
function counter() {
var n = 0;
return {
count: function() {return n++;},
reset: function() {n = 0}
};
};
var c = counter(), d = counter(); // Initialize two counter objects with different private vars (`n`)
c.count() // => 0
d.count() // => 0
c.reset() // reset() and count() share state (`n`)
c.count() // => 0
d.count() // => 1
-
call()
andapply()
allow you to indirectly invoke a function as if it were a method of some other object. -
The first argument passed is the object on which the function is to be invoked; this argument is the invocation context and becomes the value of the
this
keyword within the body of the function -
Any arguments to
call()
after the first invocation context argument are the values that are passed to the function that is invoked. E.gf.call(o, 1, 2);
-
This also applies to
apply()
except that the arguments to be passed to the function are specified as an array E.gf.apply(o, [1, 2]);
-
The primary purpose of
bind()
is to bind a function to an object. -
bind()
returns a new function
function f(y) {return this.x + y;};
var o = {x: 1};
var g = f.bind(o);
g(2) // => 3
-
bind()
also performs partial application. This means that any arguments you pass tobind()
after the first are bound along with thethis
value. This is also called currying. -
Currying basically means to use the
bind()
method to pass arguments to a function before it is invoked. Presetting arguments of a function E.g
var sum = function(x, y) {return x + y;};
var succ = sum.bind(null, 1); // bind `this` and `x`
succ(2); // => 3
function f(y, z) {return this.x + y + z};
var g = f.bind({x: 1}, 2); // bind `this` and `y`
g(3); // => 6: this.x is bound to 1, y is bound to 2 and z is 3
function multiply(number) {
'use strict';
return this * number;
}
var double = multiply.bind(2);
double(3) // => 6
double(10) // => 20
-
bind()
returns a new function that is supposed to be invoked later with a pre-configuredthis
-
apply()
and.call()
invoke the function right away. -
The function constructor (
new Function()
) doesn't use lexical scoping, instead they are always compiled as if they were top level functions. -
A higher order function is a function that operates on functions, taking one or more functions as arguments and returning a new function.
-
In JS
this
is the current execution context of a function -
this
isundefined
in a function invocation in strict mode -
In a function invocation (E.g
foo()
)this
returns thewindow
object -
this
is the newly created object in a constructor invocation (using thenew
keyword)
var numbers = {
numberA: 5,
numberB: 10,
sum: function() {
console.log(this === numbers);
function calculate() {
console.log(this === numbers);
return this.numberA + this.numberB;
}
return calculate.call(this); // or calculate.call(numbers)
}
};
numbers.sum();
function Animal(type, legs) {
this.type = type;
this.legs = legs;
this.logInfo = function() {
console.log(this === myCat);
console.log('The ' + this.type + ' has ' + this.legs + ' legs');
};
}
var myCat = new Animal('Cat', 4);
setTimeout(myCat.logInfo.bind(myCat), 1000);
// Use arrow function to preserve the context of `this` within a nested function
var numbers = {
numberA: 5,
numberB: 10,
sum: function() {
console.log(this === numbers);
const calculate = () => {
console.log(this === numbers);
return this.numberA + this.numberB;
}
return calculate();
}
}
function Animal(type, legs) {
this.type = type;
this.legs = legs;
this.logInfo = () => {
console.log(this === myCat);
console.log('The ' + this.type + ' has ' + this.legs + ' legs');
};
}
var myCat = new Animal('Cat', 4);
setTimeout(myCat.logInfo, 1000);