1 函数
1.1 什么是函数
- 函数就是具有特定功能的代码块,可以被多次调用
- 函数是 JavaScript 中的一种数据类型,属于对象类型; 使用
typeof
判断可以返回function
1.2 函数的组成
- 函数名: 函数名本质上是个变量,函数名的命名规则与变量名的命名规则一致。
- 参数: 参数本质上是个变量,只能在函数内使用,在调用函数的时候才能被赋值。
- 函数体: 用大括号包裹的代码块。
- 返回值:返回值是函数的计算结果, 作为函数调用表达式的值
1.3 声明函数
① function 关键字方式
function 函数名() {
函数体语句 ...;
}
function 函数名(参数列表...) {
函数体语句 ...;
}
② 表达式方式
var 函数名 = function() {
函数体语句 ...;
}
var 函数名 = function(参数列表...) {
函数体语句 ...;
}
1.4 函数调用和返回值
① 函数调用
-
函数名()
才是函数调用,函数体语句才能执行,才能得到函数的返回值。 - 函数名后面没有括号,就是在使用一个变量,函数体语句不会执行,也得不到函数的返回值。
② 返回值
1. 返回值是函数的计算结果,是函数调用表达式的值(函数名加括号就是函数调用表达式)
var res = fn(); // 把函数的返回值赋值给变量 res
fn() + 100; // 函数的返回值与 100 相加
console.log(fn()); // 输出函数的返回值
2. 通过 return 关键字定义函数的返回值
return 需写在函数体内,return 右边需写一个表达式(变量、直接量、带运算符的表达式), 表达式的值就是函数的返回值。
如果 return 右边是空的,函数没有返回值, 没有返回值函数调用表达式的值会自动得到 undefined
3. return 还可以结束函数的执行; 一旦执行到 return,后面的语句就不会执行了。
1.5 函数的参数
①形参和实参
形参:函数声明时设置的参数,相当于没有赋值的变量,必须以变量名的形式给出。
实参:调用函数时所给的参数,用于给形参赋值, 可以是变量、直接量、表达式等形式。
② 形参和实参的数量问题
标准情况下,形参和实参的数量应一致,按顺序赋值,如果:
形参数量 > 实参数量:后面的形参没有被赋值,自动得到 undefined。
形参数量 < 实参数量:多余的实参则没有用。
③ 形参的默认值(可选参数)
- 具有默认值的参数即可选参数
- 可选参数应放在必选参数的后面,因为实参按顺序给形参赋值,不给实参则可以返回默认值。
ES5 规范中设置默认值的方式:
function fn(name, age) {
// 如果 age 没有对应的实参, 设置一个默认值
if (age === undefined) {
age = 默认值;
}
}
ES6 规范中的设置默认值的方式:
funciton fn(name, age=默认值) {
}
④ arguments关键字
- arguments 是系统定义好的变量,可以直接使用,但只能在函数内使用, 在函数外使用则会报错。
- arguments 是个伪数组对象,里面的成员是函数调用时传进来的实参; arguments 具有length 属性,可以通过索引获取到其中的每个成员。
- arguments 是除了形参之外另外一种获取实参的方式。 形参只能获取固定数量的实参,arguments 可以获取所有的实参。
- 使用 arguments 可以定义可变参数数量的函数。
// 计算所有参数的和,返回结果
function sumFn() {
// 定义变量 记录和
var sum = 0;
// 遍历 arguments
for (var i = 0; i < arguments.length; i ++) {
sum += arguments[i];
}
// 返回结果
return sum;
}
1.6 作用域
① 变量的作用域
作用域即变量的可作用范围,根据作用域不同,变量可分为全局变量和局部变量。
全局变量: 在函数以外定义,作用域是全局。如:函数外声明的函数,函数内没有用var声明的变量。
局部变量: 在函数内定义,作用域是所在的函数。如:函数的形参,函数内声明的函数。
- 函数名本质上是变量,所以函数本身也有作用域,由函数是在哪里声明的决定。
- 如果在函数里不使用var声明变量,该变量是全局的(严格模式下不允许不使用var声明的变量的)
② 作用域链
函数里还可以继续声明函数,函数的嵌套关系形成了作用域链,函数内可以使用本层作用域和上层作用域中的变量。
作用域链描述变量查找的过程:
当使用某个变量的时候,先从本作用域中查找,如果找不到就去上层作用域查找,哪里找到哪里停止,找不到则继续向上查找,直到全局作用域,如果仍然没有查找到该变量,则报错。
注:变量的作用域只与函数声明的位置有关系,与函数在哪里调用无关。
1.7 变量提升
① 变量提升
- 代码正式执行之前,会进行预解析,预解析时会把变量提升到本作用域的最前面(只创建了变量,却没有赋值)
- 全局变量在整个代码正式执行之前就发生了提升
- 局部变量在函数体语句执行之前进行提升
② 函数提升
- 函数名本质上就是变量,所以函数也会提升到本作用域的最前面。
- 使用 function 关键字形式声明的函数,提升比较彻底,代码执行之前不但创建了函数名并且有值。
- 使用表达式方式声明的函数,提升规则与普通变量没有差别。
- 函数提升与变量提升的区别:
① 函数提升更彻底
② 正式执行代码的时候,执行到变量声明并赋值的语句会进行赋值操作, 执行到函数声明语句会跳过
1.8 匿名函数
即没有名字的函数,用函数这种数据类型的直接量表示,适合作为自调用函数和回调函数。
1.9 自调用函数(立即执行的函数)
函数声明完立即被调用,主要作用是用来产生作用域,避免全局变量污染。一般以匿名函数作为自调用函数,有名字的函数也可以作为自调用函数,但没有必要。
// 匿名的自调用函数(立即执行的函数)
(function() {
函数体语句;
})();
// 有名字的自调用函数
(function fn() {
函数体语句;
})();
// 匿名的自调用函数 设置参数
(function(形参1, 形参2) {
函数体语句;
})(实参1, 实参2);
1.10 回调函数
具有以下三个条件的函数称为回调函数:
- ① 函数是我定义的
- ② 我没有直接调用该函数
- ③ 函数却执行了
注意: 回调函数大部分情况会作为其他函数(或方法)的参数。
1.11 递归函数
函数内部再次调用自己。
① 递归成功的条件:
- 有明确的结束递归的条件
- 有一个趋向于结束递归调用的趋势
② 递归函数的缺点
- 可能发生内存泄漏
- 效率不高
③ 递归函数应用场景
- 后端的目录操作(删除、复制、剪切)
- 前端对后端数据进行递归处理