# Chapter 8. Functions **Function** = a block of JS code that's defined once but may be invoked multiple times. * *argument*: values provided by during invocation as function's parameters. * *return value*: computed by function using argument values * *invocation context*: value of `this` keyword for each invocation Utilization of functions: * **method of object**: if function is assigned to a property of an obj. * When a function is invoked on an object (e.g. `obj.method()`), this object become invocation context (aka `this`) for the function * **Constructor**: function designed to initialize a new obj. Function as **first class citizen** (object): * In JS, functions are obj. Hence, can be manipulated by programs * We can set properties on functions and invoke methods on them (i.e. pass function as parameter to method) JS function definitions can be nested within other functions ## 8.1 Defining Functions ### 8.1.1 Function Declarations **Function declaration (函数声明)** = `function` keyword + Identifier as function name + `(param1, param2, ...)` + `{JS statements as function body}` * `return` causes function to stop executing and return computed value to caller * if body has no `return`, value of function is `undefined` ```js // Print the name and value of each property of o. Return undefined. function printprops(o) { for(let p in o) { console.log(`${p}: ${o[p]}\n`); } } // Compute the distance between Cartesian points (x1,y1) and (x2,y2). function distance(x1, y1, x2, y2) { let dx = x2 - x1; let dy = y2 - y1; return Math.sqrt(dx*dx + dy*dy); } // A recursive function (one that calls itself) that computes factorials // Recall that x! is the product of x and all positive integers less than it. function factorial(x) { if (x <= 1) return 1; return x * factorial(x-1); } ``` * Name of function (in function declaration) becomes a variable, whose value is function itself. * function declaration statements are "hoisted" (level up) to top of enclosing block. * All function in a JS block will be defined before JS interpreter start execution ### 8.1.2 Function Expressions Multiple Function Expression e.g. ```js // This function expression defines a function that squares its argument. // Note that we assign it to a variable const square = function(x) { return x*x; }; // Function expressions can include names, which is useful for recursion. const f = function fact(x) { if (x <= 1) return 1; else return x*fact(x-1); }; // Function expressions can also be used as arguments to other functions: [3,2,1].sort(function(a,b) { return a-b; }); // Function expressions are sometimes defined and immediately invoked: let tensquared = (function(x) {return x*x;}(10)); ``` **Function Expression (FE 函数表达式)**: * FE appear within context of a larger expression, or within statement * name of function in FE is *optional*. (e.g. 1st FE e.g. has no function name) * FE/FD declare variable: * How FD use variable: (follow e.g. in 8.1.1 `function factorial(x)`) declares a variable and assigns a function obj to it. * How FE use variable: developer can decide whether assign the newly defined function obj to a const or var, so we can refer to it mult-times later. (e.g. 3rd & 5th FE e.g. does not assign function obj to obj, and directly use it) * Good practice: assign FE to `const` to protect function obj. **FD vs. FE**: * Function defined by FD: the func obj are created before the script get executed (i.e. hoisted), so we can call these functions from code that appears above FD. * Functions defined by FE **DO NOT EXIST** until FE are evaluated. * To invoke a function (either defined using FE/FD), JS must can refer to it, function defined by FE cannot be referred until it's assigned to a variable ### 8.1.3 Arrow Functions In ES6, **Arrow Function** provide more compact function syntax. **Use arrow => to separate function parameters from function body** Syntax of arrow function: * *general form*: comma-separated list of params in parentheses, followed by `=>` arrow, followed by function body in curly braces ```js const func_var = (param1, param2) => { return param1 + param2; }; ``` * *compact form (single return)*: if function body is a single `return` statement, omit `return`, semicolon, and curly braces ```js const func_var = (param1, param2) => param1 + param2; ``` * *compact form (1 param)*: if arrow func has only 1 param, omit `()` ```js const func_var = param => param*param + 2*param; ``` * *compact form (no param)*: if arrow func has no param, `()` must be there ```js const constantFunc = () => 42; ``` Additional Syntax rules: * **No newline btw `(param)` and `arrow`**: it will create valid statement with other meaning (e.g. `const polynomial = x`) * **{} must needed in body for single return statement**: it will avoid syntactic ambiguity: ```js const f = x => { return { value: x }; }; // Good: f() returns an object const g = x => ({ value: x }); // Good: g() returns an object const h = x => { value: x }; // Bad: h() returns nothing const i = x => { v: x, w: x }; // Bad: Syntax Error ``` #### Where to use arrow function? It's like lambda function in Python, it's ideal to pass arrow function to another function ```js // Make a copy of an array with null elements removed. let filtered = [1,null,2,3].filter(x => x !== null); // filtered == [1,2,3] ``` #### Arrow Func vs other Funcs * Arrow func inherit value of `this` from the environment (where it's defined) * Arrow func do not have a `prototype` property, hence it cannot be used as constructor for new classes. ### 8.1.4 Nested Functions ```js function hypotenuse(a, b) { function square(x) { return x*x; } return Math.sqrt(square(a) + square(b)); } ``` Scoping rule of nested function: enclosure function can access param and var of the functions they are nested within (i.e. inner function know outer function's param)