JavaScript functions are first-class language' elements in the sense that they are plain objects.
Function
is the constructor (i.e., runtime type) of function objects.Rule(s)
- The absence of static typing in JavaScript suppresses any control about functions' arguments and returned objects: their types and number.
- Functions may return values while they do not declare any
return
.const f = function () { /* <=> 'return;' <=> 'return undefined;' */ }; console.assert(f instanceof Function); console.assert(typeof f === 'function'); console.assert(f.constructor === Function);
Example
const my_function = function (my_parameter) { // Forbidden in strict mode (https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/arguments): // console.log(arguments.callee); return ++my_parameter; }; console.log(my_function(0)); // '1' is displayed...
Arguments' default values
Rule(s)
- Functions support default values for (right-most) arguments as in C++, Java, etc.
Example
// 'Run' as a static function in a class: static Run(name = "FranckBarbier", job = "Trainer") { …
Anonymous functions
Rule(s)
- Anonymous functions have no name. Similar to lambda expressions, they are introduced for on-the-fly needs.
- In JavaScript, one may use inside a function the predefined
arguments
variable.arguments[0]
represents the first argument (if any),arguments[1]
represents… Etc.Example
const f = function (g) { // 'f' is defined... console.log("'arguments' is a predefined array that lets the access to the passed arguments: " + arguments.length); g(); }; f(function () { // 'f' is called with anonymous function as single argument... console.log("This no-name function plays the role of 'g' in 'f'"); });
Lambda expressions
Rule(s)
- Similar to Java, C++…, JavaScript supports lambda expressions; they lighten the way by which anonymous functions are handled in the code.
Example
const f = (g, i = 0) => { // 'f' is defined by means of a lambda expression console.log(g(i)); }; // Calling f... f(function (my_i) { // 'f' is called with an anonymous function as parameter return ++my_i; }/*, 1*/); // Calling f, another style... f(my_i => { // 'f' is called with a lambda function as parameter return ++my_i; }/*, 1*/); // Calling f, yet another style... f(my_i => ++my_i/*, 1*/); // Implicit 'return'
Variable number of arguments
Rule(s)
- Same to Java, C++…, JavaScript (from JavaScript 6) supports the idea of having a variable number of arguments in the form of arrays.
Example
const h = function (a = -1, ...bs /*, c*/ /* <- error! */) { let sum = a; bs.forEach(b => { console.log("b: " + b); sum += b; }); console.log("Sum: " + sum); }; // Note that default value for 'a' is useless: h(1, 2, 3); // 'Sum: 6' is displayed, i.e., '1' is substituted for 'a' h(...[1, 2, 3]); // <=> 'h(1, 2, 3);'
Rule(s)
- In TypeScript, a variable number of parameters is based on the
…
syntax as well.Example Lottery.ts.zip
class Lottery { public static Draw(bonus_number: number, ...numbers: Array<number>) /* Return type is inferred... */ { … } … let result = Lottery.Draw(6, 43, 12, 3, 32, 34, 22); console.assert(result.draw[0] === 43);
Meta-functions
Rule(s)
- Meta-functions are part of the
Function
constructor itself:call
andapply
.Example
const her_function = function (her_parameter) { console.log("'this': " + this + " 'her_parameter': " + her_parameter); // Forbidden in strict mode (https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/arguments/): // console.log(arguments.callee); }; // First arg. plays the role of 'this' inside the function while '0' is substituted for 'her_parameter': her_function.call("Catherine", 0); // "'this': Catherine 'her_parameter': 0" is displayed... // First arg. plays the role of 'this' inside the function while '[0]' is a one-element array containing '0' as unique element: her_function.apply("Catherine", [0]); // "'this': Catherine 'her_parameter': 0" is displayed...
Generator functions
Rule(s)
- Generator functions are used in conjunction with the possibility of iterating over any object provided that the latter one implements an iterator protocol (see also more on generators … and more on iterators…)
Example Generator.ts.zip
// 'Meal' type has a method named '@@iterator' ('Symbol.iterator' <=> '@@iterator'), which is a generator function: type Meal = { when: string, taste: string, [Symbol.iterator](): IterableIterator<string> }; const meal: Meal = { when: "today", taste: "medium", [Symbol.iterator]: function* (from = "yesterday") { // Key 'Symbol.iterator' (i.e., '@@iterator') is added as 'Generator' object yield from; yield "today"; yield "tomorrow"; } }; const {when: first_part, ...remaining_part} = meal; console.info(first_part); // 'today' console.info(JSON.stringify(remaining_part)); // '{"taste":"medium"}' console.info(Object.getOwnPropertySymbols(meal)[0] === Symbol.iterator); // 'true' const iterator: IterableIterator<string> = meal[Symbol.iterator](); console.info(iterator.next().value); // 'yesterday' // Spread syntax only applies on iterable objects (https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Spread_syntax): console.info([...meal]); // '[ 'yesterday', 'today', 'tomorrow' ]' for (const value of meal) console.info(value); // 'yesterday' 'today' 'tomorrow'
The case of
this
Rule(s)
this
may refer to different objects (includingwindow
) depending upon the execution context. JavaScript offers means to control to whatthis
has to refer to (see also here…).Example (PURE JavaScript)
const o = {}; Object.defineProperty(o, "simple_array", { value: [null, 0, "ABC"], enumerable: true, configurable: true, writable: true }); o.simple_array_processing = function () { for (let i = 0; i < this.simple_array.length; i++) this.simple_array[i] = null; // Great, 'this' is set to the appropriate "current" object, i.e., 'o'! }; o.simple_array_processing(); // Call... console.log(JSON.stringify(o.simple_array)); // '[null,null,null]' o.simple_array_processing = function () { // Erase prior version... this.simple_array.forEach(function (element) { console.assert(this !== o); console.log("What is 'this'? " + Object.getOwnPropertyNames(this).join(" - ")); } /* ,this */); // Uncommenting leads to 'this === o' }; o.simple_array_processing(); // Call... o.simple_array_processing = function () { // Erase prior version... this.simple_array.forEach(element => { // 'this' in lambda expression is set *IN A STATIC WAY* console.assert(this === o); console.log("What is 'this'? " + Object.getOwnPropertyNames(this).join(" - ")); } /* ,this */); // No need of injecting 'this' as second argument of 'forEach' }; o.simple_array_processing(); // Call...
Rule(s)
- Using
bind
is a way to inject whatthis
has to refer to.Example (PURE JavaScript)
const my_iteration = function (element) { console.assert(this === o); console.log("What is 'this'? " + Object.getOwnPropertyNames(this).join(" - ")); }; o.simple_array_processing = function () { // Erase prior version... this.simple_array.forEach(my_iteration.bind(this)); }; o.simple_array_processing(); // Call...