# 函数

## 定义函数

### 函数声明

• 函数的名称。
• 函数参数列表，包围在括号中并由逗号分隔。
• 定义函数的 JavaScript 语句，用大括号`{}`括起来。

```function square(number) {
return number * number;
}
```

```return number * number;
```

```function myFunc(theObject) {
theObject.make = "Toyota";
}

var mycar = {make: "Honda", model: "Accord", year: 1998};
var x, y;

x = mycar.make;     // x获取的值为 "Honda"

myFunc(mycar);
y = mycar.make;     // y获取的值为 "Toyota"
// (make属性被函数改变了)
```

### 函数表达式

``````const square = function(number) { return number * number; };
var x = square(4); // x gets the value 16``````

```const factorial = function fac(n) {return n<2 ? 1 : n*fac(n-1)};

console.log(factorial(3));
```

```function map(f,a) {
let result = []; // 创建一个数组
let i; // 声明一个值，用来循环
for (i = 0; i != a.length; i++)
result[i] = f(a[i]);
return result;
}
```

``````function map(f, a) {
let result = []; // 创建一个数组
let i; // 声明一个值，用来循环
for (i = 0; i != a.length; i++)
result[i] = f(a[i]);
return result;
}
const f = function(x) {
return x * x * x;
}
let numbers = [0,1, 2, 5,10];
let cube = map(f,numbers);
console.log(cube);``````

```var myFunc;
if (num == 0){
myFunc = function(theObject) {
theObject.make = "Toyota"
}
}```

## 调用函数

```square(5);
```

```console.log(square(5));
/* ... */
function square(n) { return n*n }
```

``````console.log(square); // square is hoisted with an initial value undefined.
console.log(square(5)); // Uncaught TypeError: square is not a function
const square = function (n) {
return n * n;
}``````

```function factorial(n){
if ((n == 0) || (n == 1))
return 1;
else
return (n * factorial(n - 1));
}
```

```var a, b, c, d, e;

a = factorial(1); // 1赋值给a
b = factorial(2); // 2赋值给b
c = factorial(3); // 6赋值给c
d = factorial(4); // 24赋值给d
e = factorial(5); // 120赋值给e
```

## 函数作用域

```// 下面的变量定义在全局作用域(global scope)中
var num1 = 20,
num2 = 3,
name = "Chamahk";

// 本函数定义在全局作用域
function multiply() {
return num1 * num2;
}

multiply(); // 返回 60

// 嵌套函数的例子
function getScore() {
var num1 = 2,
num2 = 3;

return name + " scored " + (num1 + num2);
}

}

getScore(); // 返回 "Chamahk scored 5"
```

## 作用域和函数堆栈

### 递归

1. 函数名
2. `arguments.callee`
3. 作用域下的一个指向该函数的变量名

```var foo = function bar() {
// statements go here
};
```

1. `bar()`
2. `arguments.callee()` （译者注：ES5禁止在严格模式下使用此属性）
3. `foo()`

```var x = 0;
while (x < 10) { // "x < 10" 是循环条件
// do stuff
x++;
}
```

```function loop(x) {
if (x >= 10) // "x >= 10" 是退出条件（等同于 "!(x < 10)"）
return;
// 做些什么
loop(x + 1); // 递归调用
}
loop(0);
```

```function walkTree(node) {
if (node == null) //
return;
// do something with node
for (var i = 0; i < node.childNodes.length; i++) {
walkTree(node.childNodes[i]);
}
}
```

`loop`函数相比，这里每个递归调用都产生了更多的递归。

```function foo(i) {
if (i < 0)
return;
console.log('begin:' + i);
foo(i - 1);
console.log('end:' + i);
}
foo(3);

// 输出:

// begin:3
// begin:2
// begin:1
// begin:0
// end:0
// end:1
// end:2
// end:3```

### 嵌套函数和闭包

• 内部函数只可以在外部函数中访问。
• 内部函数形成了一个闭包：它可以访问外部函数的参数和变量，但是外部函数却不能使用它的参数和变量。

```function addSquares(a, b) {
function square(x) {
return x * x;
}
return square(a) + square(b);
}
a = addSquares(2, 3); // returns 13
b = addSquares(3, 4); // returns 25
c = addSquares(4, 5); // returns 41```

```function outside(x) {
function inside(y) {
return x + y;
}
return inside;
}
fn_inside = outside(3); // 可以这样想：给一个函数，使它的值加3
result = fn_inside(5); // returns 8

result1 = outside(3)(5); // returns 8```

### 多层嵌套函数

```function A(x) {
function B(y) {
function C(z) {
console.log(x + y + z);
}
C(3);
}
B(2);
}
A(1); // logs 6 (1 + 2 + 3)```

1. B形成了一个包含A的闭包，B可以访问A的参数和变量
2. C形成了一个包含B的闭包
3. B包含A，所以C也包含A，C可以访问B和A的参数和变量。换言之，C用这个顺序链接了B和A的作用域

### 命名冲突

```function outside() {
var x = 5;
function inside(x) {
return x * 2;
}
return inside;
}

outside()(10); // returns 20 instead of 10```

## 闭包

```var pet = function(name) {          //外部函数定义了一个变量"name"
var getName = function() {
//内部函数可以访问 外部函数定义的"name"
return name;
}
//返回这个内部函数，从而将其暴露在外部函数作用域
return getName;
};
myPet = pet("Vivie");

myPet();                            // 返回结果 "Vivie"
```

```var createPet = function(name) {
var sex;

return {
setName: function(newName) {
name = newName;
},

getName: function() {
return name;
},

getSex: function() {
return sex;
},

setSex: function(newSex) {
if(typeof newSex == "string"
&& (newSex.toLowerCase() == "male" || newSex.toLowerCase() == "female")) {
sex = newSex;
}
}
}
}

var pet = createPet("Vivie");
pet.getName();                  // Vivie

pet.setName("Oliver");
pet.setSex("male");
pet.getSex();                   // male
pet.getName();                  // Oliver
```

```var getCode = (function(){
var secureCode = "0]Eal(eh&2";    // A code we do not want outsiders to be able to modify...

return function () {
return secureCode;
};
})();

getCode();    // Returns the secret code
```

```var createPet = function(name) {  // Outer function defines a variable called "name"
return {
setName: function(name) {    // Enclosed function also defines a variable called "name"
name = name;               // ??? How do we access the "name" defined by the outer function ???
}
}
}
```

## 使用 arguments 对象

```arguments[i]
```

``````function myConcat(separator) {
var result = ''; // 把值初始化成一个字符串，这样就可以用来保存字符串了！！
var i;
// iterate through arguments
for (i = 1; i < arguments.length; i++) {
result += arguments[i] + separator;
}
return result;
}``````

```// returns "red, orange, blue, "
myConcat(", ", "red", "orange", "blue");

// returns "elephant; giraffe; lion; cheetah; "
myConcat("; ", "elephant", "giraffe", "lion", "cheetah");

// returns "sage. basil. oregano. pepper. parsley. "
myConcat(". ", "sage", "basil", "oregano", "pepper", "parsley");
```

## 函数参数

### 默认参数

```function multiply(a, b) {
b = (typeof b !== 'undefined') ?  b : 1;

return a*b;
}

multiply(5); // 5
```

```function multiply(a, b = 1) {
return a*b;
}

multiply(5); // 5```

### 剩余参数

```function multiply(multiplier, ...theArgs) {
return theArgs.map(x => multiplier * x);
}

var arr = multiply(2, 1, 2, 3);
console.log(arr); // [2, 4, 6]```

## 箭头函数

### 更简洁的函数

``````var a = [
"Hydrogen",
"Helium",
"Lithium",
"Beryllium"
];

var a2 = a.map(function(s){ return s.length });

console.log(a2); // logs [ 8, 6, 7, 9 ]

var a3 = a.map( s => s.length );

console.log(a3); // logs [ 8, 6, 7, 9 ]``````

### `this` 的词法

``````function Person() {
// 构造函数Person()将`this`定义为自身
this.age = 0;

setInterval(function growUp() {
// 在非严格模式下，growUp()函数将`this`定义为“全局对象”，
// 这与Person()定义的`this`不同，
// 所以下面的语句不会起到预期的效果。
this.age++;
}, 1000);
}

var p = new Person();``````

``````function Person() {
var self = this; // 有的人习惯用`that`而不是`self`，
// 无论你选择哪一种方式，请保持前后代码的一致性
self.age = 0;

setInterval(function growUp() {
// 以下语句可以实现预期的功能
self.age++;
}, 1000);
}``````

``````function Person(){
this.age = 0;

setInterval(() => {
this.age++; // 这里的`this`正确地指向person对象
}, 1000);
}

var p = new Person();``````

## 预定义函数

JavaScript语言有好些个顶级的内建函数：

`eval()`

`eval()`方法会对一串字符串形式的JavaScript代码字符求值。

`uneval()`

`uneval()`方法创建的一个`Object`的源代码的字符串表示。

`isFinite()`

`isFinite()`函数判断传入的值是否是有限的数值。 如果需要的话，其参数首先被转换为一个数值。

`isNaN()`

`isNaN()`函数判断一个值是否是`NaN`。注意：`isNaN`函数内部的`强制转换规则`十分有趣； 另一个可供选择的是ECMAScript 6 中定义`Number.isNaN()` , 或者使用 `typeof`来判断数值类型。

`parseFloat()`

`parseFloat()` 函数解析字符串参数，并返回一个浮点数。

`parseInt()`

`parseInt()` 函数解析字符串参数，并返回指定的基数（基础数学中的数制）的整数。

`decodeURI()`

`decodeURI()` 函数对先前经过`encodeURI`函数或者其他类似方法编码过的字符串进行解码。

`decodeURIComponent()`

`decodeURIComponent()`方法对先前经过`encodeURIComponent`函数或者其他类似方法编码过的字符串进行解码。

`encodeURI()`

`encodeURI()`方法通过用以一个，两个，三个或四个转义序列表示字符的UTF-8编码替换统一资源标识符（URI）的某些字符来进行编码（每个字符对应四个转义序列，这四个序列组了两个”替代“字符）。

`encodeURIComponent()`

`encodeURIComponent()` 方法通过用以一个，两个，三个或四个转义序列表示字符的UTF-8编码替换统一资源标识符（URI）的每个字符来进行编码（每个字符对应四个转义序列，这四个序列组了两个”替代“字符）。

`escape()`

`unescape()`