// 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.
* 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
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)
## 8.2 Invoking Functions
JS will not execute function body, when function is defined. Rather executed when func is invoked
5 ways to invoke JS functions:
* As functions
* As methods
* As constructors
* Indirectly through `call()`, `apply()`
* Implicitly
### 8.2.1 Function Invocation
**Invocation Expression**:
```js
func_name(param1, param2);
```
* params can be any argument expression. JS will evaluate these expression and then use result as args.
* For no `return` function, value of return is `undefined`
Use **conditional invocation** on invocation expression: invoke the function only if it's not `null` or `undefined`
A **method** = JS function stored in a property of an object.
* **Defining a function method**, given object `o`, method name `m`, and a function `f`:
```js
o.m = f;
```
* **Invoking object method**:
```js
o.m(param1, param2);
```
Method invokation can also use `[]` instead of dot notation:
```js
o["m"](x,y); // Another way to write o.m(x,y).
a[0](z) // Also a method invocation (assuming a[0] is a function).
```
**Invoctaion Context** of invoking by method:
* (OOP) In a method-invocation expression, the object become invocation contaxt, the function body can refer to the object by keyword `this` (e.g. shown below)
```js
let calculator = { // An object literal
operand1: 1,
operand2: 1,
add() { // We're using method shorthand syntax for this function
// Note the use of the this keyword to refer to the containing object.
this.result = this.operand1 + this.operand2;
}
};
calculator.add(); // A method invocation to compute 1+1.
calculator.result // => 2
```
*`this` is a keyword, not a variable or property name.
#### Nested function & `this` keyword
* nested functions do not inherit the this value of the containing function.
* If a nested function is invoked as a method, its this value is the object it was invoked on.
* If a nested function (that is not an arrow function) is invoked as a function, then its this value will be either the global object (non-strict mode) or undefined (strict mode).
* It is a common mistake to assume that a nested function defined within a method and invoked as a function can use this to obtain the invocation context of the method.
Solution (workaround) 1:
```js
let o = { // An object o.
m: function() { // Method m of the object.
let self = this; // Save the "this" value in a variable.
this === o // => true: "this" is the object o.
f(); // Now call the helper function f().
function f() { // A nested function f
this === o // => false: "this" is global or undefined
self === o // => true: self is the outer "this" value.
}
}
};
o.m(); // Invoke the method m on the object o.
```
* Within the method m, we assign the this value to a variable self, and within the nested function f, we can use self instead of this to refer to the containing object.
Solution (workaround) 2 since ES6 **arrow function**:
```js
const f = () => {
this === o // true, since arrow functions inherit this
};
```
* Functions defined as expressions instead of statements are not hoisted, so in order to make this code work, the function definition for f will need to be moved within the method m so that it appears before it is invoked.
Solution (workaround) 3 using `.bind(this)`:
```js
const f = (function() {
this === o // true, since we bound this function to the outer this
}).bind(this);
```
### 8.2.3 Constructor Invocation
**Constructor invocation** = function/method invocation proceded by keyword `new`.
* Constructor invocation differ from regular function and method invocations in arg handling, invocation context, and return value.
* A constructor invocation creates a new, empty object that inherits from the object specified by the prototype property of the constructor.
* Constructor functions are intended to initialize objects, and this newly created object is used as the invocation context, so the constructor function can refer to it with the this keyword.