Javascript: Functions
anonymous function is function without name.
function is stored as property of an object - method. method can use this to access the object so that it can retrieve values from the object or modify the object. public methods - methods that get their object context from this
when function is invoked with this pattern, this is bound to the global object method cannot employ an inner function to help it do its work because the inner function does not share the method’s access to the object as its this is bound to the wrong value. But if the method defines a variable and assings it the value of this, the inner function will have access to this through that variable
// Augmented myObject with a double method
myObject.double = function () {
var that = this; // workaround
var helper = function () {
that.value = add(that.value, that.value);
};
helper(); // Invoke helper as a function.
};
// Invoke double as a method
myObject.double();
document.writeln(myObject.value);
// Create a constructor function called Quo.
// It makes an object with a status property
var Quo = function (string) {
this.status = string;
};
// Give all instances of Quo a public method
// called get_status.
Quo.prototype.get_status = function () {
return this.status;
};
// Make an instance of Quo
var myQuo = new Quo('confused');
document.writeln(myQuo.get_status()); // confused
The apply method lets us construct an array of arguments to use to invoke a functions. It also lets us choose the value ot this.
param1 - value that should be bound to this
param2 - array of parameters
// Make an array of 2 numbers and add them
var array = [3, 4];
var sum = add.apply(null, array); // sum is 7
// Make an object with a status member.
var statusObject = {
status: 'A-OK'
};
// statusObject does not inherit from Quo.prototype,
// but we can invoke the get_status method on
// statusObject even though statusObject does not have
// a get_status method
var status = Quo.prototype.get_status.apply(statusObject);
// status is 'A-OK'
arguments is not really an array. It is an array-like object. it has a length property, but it lacks all of the array methods.
// Make a function that adds a lot of stuff
// Note that defining the variable sum inside of
// the function does not interfere with the sum
// defined outside of the function. The function
// only sees the inner one.
var sum = function() {
var i, sum = O;
for (i = 0; i < arguments.length; i += 1) {
sum += arguments[i];
}
return sum;
};
document.writeln(sum(4, 8, 15, 16, 23, 42)); // 108
// The return statement can be used to cause the function to retrun early.
// When return is executed, the function returns immediately without executing
// the remaining statements.
// A function always returns a value. If the return value is not specified, then undefined is returned.
// If the function was invoked with the new prefix and the return value is not an object,
// then this (the new object) is returned insted.
Exceptions
var add = function (a, b) {
if (typeof a !== 'number' || typeof b !== 'number') {
throw {
name: 'TypeError',
message: 'add needs numbers'
};
}
return a + b;
};
// Make a try_it function that calls the new add function incorrectly
var try_it = function () {
try {
add("seven");
} catch (e) {
document.writeln(e.name + ': ' + e.message);
}
};
try_it();
// Add a method only if the method is known to be missing
Function.prototype.method = function (name, func) {
if (!this.prototype[name]) {
this.prototype[name] = func;
return this;
}
};
Number.method('integer', function() {
return Math[this < 0 ? 'ceil' : 'floor'](this);
});
document.writeln((-10 / 3).integer()); // -3
// method that removes spaces from the ends of a string
String.method('trim', function () {
return this.replace(/^\s+|\s|$/g, '');
});
document.writeln('"' + " neat ".trim() + '"');
//The Towers of Hanoi
var hanoi = function hanoi(disc, src, aux, dst) {
if (disc > 0) {
hanoi(disc - 1, src, dst, aux);
document.writeln('Move disc ' + disc + ' from ' + src + ' to ' + dst);
hanoi(disc - 1, aux, src, dst);
}
};
hanoi(3, 'Src', 'Aux', 'Dst');
// Define a walk_the_DOM function that visits every
// node of the tree in HTML source order, starting
// from some given node. It invokes a function,
// passing it each node in turn. walk_the_DOM calls
// itself to process each of the child nodes.
var walk_the_DOM = function walk(node, func) {
func(node);
node = node.firstChild;
while(node) {
walk(node, func);
node = node.nextSibling;
}
};
// Define a getElementsByAttribute function. It
// takes an attribute name string and an optional
// matching value. It calls walk_the_DOM, passing it a
// function that looks for an attribute name in the node.
// The maching nodes are accumulated in a results array.
var getElementsByAttribute = function (att, value) {
var results = [];
walk_the_DOM(document.body, function (node) {
var actual = node.nodeType === 1 && node.getAttribute(att);
if(typeof actual === 'string' &&
(actual === value || typeof value !== 'string')) {
results.push(node);
}
});
return results;
};
var foo = function () {
var a = 3, b = 5;
var bar = function () {
var b = 7, c = 11;
// At this point, a is 3, b is 7, c is 11
a += b + c;
// At this point, a is 21, b is 7, c is 11
};
// At this point, a is 3, b is 5, c is not defined
bar();
// At this point, a is 21, b is 5
};
JavaScript does have function scope. That means that the parameters and variables defined in a function are not visible outside of the function and that a variable defined anywhere within a function is visible everywhere within the function. It’s best to declare all of the variables used in a function at the top of the function body.
var myObject = (function () {
var value = O;
return {
increment: function (inc) {
value += typeof inc === 'number' ? inc : 1;
},
getValue: function () {
return value;
}
};
}());
// Create a maker function called quo. It makes an
// object with a get_status method and a private
// status property.
var quo = function (status) {
return {
get_status: function() {
return status;
}
};
};
// Make an instance of quo
var myQuo = quo("amazed");
document.writeln(myQuo.get_status());
// Define a function that sets a DOM node's color
// to yellow and then fades it to white
var fade = function (node) {
var level = 1;
var step = function () {
var hex = level.toString(16);
node.style.backgroundColor = '#FFFF' + hex + hex;
if (level < 15) {
level += 1;
setTimeout(step, 100);
}
};
setTimeout(step, 100);
};
fade(document.body);
request = prepare_the_request();
response = send_request_synchronously(request);
display(response);
request = prepare_the_request();
send_request_asynchronously(request, function(response) {
display(response);
});
String.method('deentityify', function() {
// The entity table. It maps entity names to
// characters.
var entity = {
quot: '"',
lt: '<',
gt: '>'
};
// Return the deentityify method.
return function () {
// This is the deentityify method. It calls the string
// replace method, looking for substrings that start
// with '&' and end with ';'. If the characters in
// between are in the entity table, then replace the
// entity with the character from the table. It uses
// a regular expression (Chapter 7).
return this.replace(/&[^&;]+);/g,
function(a, b) {
var r = entity[b];
return typeof r === 'string' ? r : a;
}
);
};
}());
document.writeln('<">'.deentityify()); // <">
Function.method('curry', function() {
var slice = Array.prototype.slice,
args = slice.apply(arguments),
that = this;
return function() {
return that.apply(null, args.concat(slice.apply(arguments)));
};
});
Functions can use objects to remember the results of previous operations, making it possible to avoid unnecessary work. This is called memoization.
var fibonacci = (function() {
var memo = [0, 1];
var fib = function() {
var result memo[n];
if(typeof result !== 'number'){
result = fib(n-1) + fib(n-2);
memo[n] = result;
}
return result;
};
return fib;
}());
// the memoizer function will take an initial memo array and the formula function.
// It returns a recur function that manages the memo store and that calls the formula
// function as needed. We pass the recur function and the function's paramters to the
// formula function:
var memoizer = function (memo, formula) {
var recur = function(n) {
var result = memo[n];
if(typeof result !== 'number') {
result = formula(recur, n);
memo[n] = result;
}
return result;
};
return recur;
};
// We can now define fibonacci with the memoizer, providing the initial memo array and formula function
var fibonacci = memoizer([0, 1], function(recur, n) {
return recur(n-1) + recur(n-2);
});