[TOC]
* * * * *
> 定义:
函数是 JavaScript 中的基本组件之一。 一个函数是 JavaScript 过程 :一组执行任务的语句。
### 1. 定义函数
~~~
定义一个函数并不会自动的执行它。
定义了函数仅仅是赋予函数以名称并明确函数被调用时该做些什么。
调用函数才会以给定的参数真正执行这些动作。
function f1(arg1,arg2) {}
var a1=function f1(arg1,arg2) {}
var a2=function (arg1,arg2) {}
f1.name=>'f1';
a1.name=>'f1';
a2.name=>'a2';
~~~
* * * * *
### 2. 参数
>[danger]原始值参数(比如一个具体的数字)被作为值传递给函数;值被传递给函数,如果被调用函数改变了这个参数的值,这样的改变不会影响到全局或调用函数。
>[danger]如果你传递一个对象(即一个非原始值,例如Array或用户自定义的对象)作为参数,而函数改变了这个对象的属性,这样的改变对函数外部是可见的。
~~~
在函数体内,使用arguments对象获取类数组对象的参数列表, arguments[0] 就表示第一个传进来的参数,如果在函数体内修改arguments[0],同步的表示第一个参数的变量值也会变,并不是说他们引用相同的内存,而是值会同步;
arguments变量只是类数组对象,并不是一个数组。
称其为类数组对象是说它有一个索引编号和 length (得到参数的个数)属性。
尽管如此,它并不拥有全部的Array对象的操作方法。
~~~
~~~
函数的参数,如果没传(由于可传可不传),或者不是需要的类型,
为了程序的健壮
在ES5中针对这种情况就要做判断,专门给参数赋默认值:
function multiply(a, b) {
b = (typeof b !== 'undefined') ? b : 1;
return a*b;
}
multiply(5); // 5
在ES6中对这种操作做了规范,更容易:
function multiply(a, b = 1) {
return a*b;}
multiply(5); // 5
~~~
* * * * *
### 3. 函数作用域
一个函数可以访问在其所在定义域内的所有变量和函数;
如果一个函数定义在全局定义域内,那么它就可以访问全局作用域内的所有变量和函数;
如果一个函数定义在另外一个函数内,那么它就可以访问父函数内的所有变量和函数;
函数中的 `this` 引用的是函数据以执行的环境对象
* * * * *
### 4. 返回值
函数总是有返回值得
![](https://box.kancloud.cn/5978c04f2ccc706c92b020139a7c1577_429x60.png)
![](https://box.kancloud.cn/2e919fec4bd7d2e50cfbd2b931fe0fc7_409x221.png)
* * * * *
### 5. 递归调用
~~~
一个函数可以指向并调用自身。有三种方法可以达到这个目的:
1. 函数名
2. arguments.callee
3. 作用域下的一个指向该函数的变量名
比如:
var a=function f3(x) {
/*
那么在函数体内,a(y), f3(y),arguments.callee(y) 是相等的,
都是以y为参数,执行函数
*/
}
~~~
当然,在函数体内调用自身的函数就是递归,为了避免无限循环,我们就需要设定执行条件。
分析下一个递归调用
![](https://box.kancloud.cn/f902bb0f95dc5c4a38b497eea9e2ab7c_429x453.png)
~~~
将程序的执行过程写出来,就像顺序执行代码:
if(3<0) return;
console.log('begin:', 3);
{
if(2<0) return;
console.log('begin:', 2);
{
if(1<0) return;
console.log('begin:', 1);
{
if(0<0) return;
console.log('begin:', 0);
{
if(-1<0) return; //这一条件符合,跳出递归
}
console.log('end:', 0);
}
console.log('end:', 1);
}
console.log('end:', 2);
}
console.log('end:', 3);
~~~
* * * * *
### 6. 函数的闭包
可以在一个函数里面嵌套另外一个函数。嵌套(内部)函数对其容器(外部)函数是私有的。它自身也形成了一个闭包。一个闭包是一个可以自己拥有独立的环境与变量的的表达式(通常是函数)。
既然嵌套函数是一个闭包,就意味着一个嵌套函数可以”继承“容器函数的参数和变量。换句话说,内部函数包含外部函数的作用域。
~~~
总结:
1. 内部函数只可以在外部函数中访问。
2.内部函数形成了一个闭包:它可以访问外部函数的参数和变量,但是外部函数却不能使用它的参数和变量。
~~~
* * * * *
### 7. 箭头函数:
箭头函数表达式(也称胖箭头函数)相比函数表达式具有较短的语法并以词法的方式绑定 this。箭头函数总是匿名的。
优点:
有两个因素会影响引入箭头函数:更简洁的函数和 this。
~~~
setTimeout/setInterval(function() {
//这里的this自动绑定到顶层作用域
});
function f1() {
setTimeout(function() {
console.log(this.name);
});
}
function f2() {
setTimeout(()=>{
console.log(this.age);
});
}
window.name='global';
window.age=25;
var o={
name: 'local',
age: 26,
showName: f1,
showAge: f2
};
o.showName()
'global'
o.showAge()
26
~~~
* * * * *
### 8. 参数和函数内部定义的函数名相同的情况
![](https://box.kancloud.cn/a0082e923cd7f40a8b51028a63356089_351x213.png)
### 9. 严格模式
~~~
当函数的执行作用域为全局作用域:
1. 非严格模式下 , this 默认指向window;
2. 严格模式下,this 为 undefined;
~~~
![](https://box.kancloud.cn/eff278c704a26892bdfa4e06069f2803_427x479.png)
* * * * *
### 10. 高阶函数
> `Higher-order function`
> 两个主要特征:
> 1.函数可以作为参数被传递
> 2.函数可以作为返回值输出
>[info] 题 1:设计一个函数,输入索引就可以得到值
|index|0|1|2|3|4|5|
| --- | --- | --- | --- | --- | --- | --- |
| value |1|1|2|3|5|8|
~~~
使用递归方法
function f1(n) {
if(n<2) return 1;
else {
return f1(n-2)+f1(n-1)
//这里为了减少耦合可用 arguments.callee来代替f1
}
}
但是这样存在性能问题,所以我们需要缓存计算值
function f2(f,n) {
var result={};
if(result[n]===undefined) {
result[n]=f(n);
}
return result[n];
}
这里就把把函数当做参数传递了进去
~~~
>[info]题 2:构造一个简单的闭包(返回值为函数)
~~~
function f3(arg1) {
var o={
name: arg1
};
return function() {
return o.name;
}
}
var a=f3('wdd'); //此时a是函数引用
a(); //输出 'wdd'
~~~