2 函数
2.1 函数概述
① 什么是函数
1. 函数就是具有特定功能的代码块,可以被多次调用
2. 函数是 JS 中的一种数据类型,属于对象类型; 使用 typeof 判断可以返回 function
② 函数的作用
1)解决代码的冗余问题,形成代码复用。
2)可以把整个代码项目,进行函数模块化。
3)封装代码,让函数内部的代码对外部不可见。
③ 函数的组成
函数名: 函数名本质上就是个变量,函数名的命名规则就是变量名的命名规则。
参数: 参数本质上就是个变量,只能在函数内使用,在调用函数的时候才能赋值。
函数体: 用大括号包裹的代码块。
**返回值:**返回值是函数的计算结果, 作为函数调用表达式的值
2.2 声明函数的方式
① function 关键字方式
function 函数名() {
函数体语句 ...;
}
function 函数名(参数列表...) {
函数体语句 ...;
}
② 表达式方式
var 函数名 = function() {
函数体语句 ...;
}
var 函数名 = function(参数列表...) {
函数体语句 ...;
}
2.3 函数的调用和返回值
① 函数调用
1. 函数名后面加括号才是函数调用,函数体语句才会执行,才能得到函数的返回值。
2. 函数名后面没有括号,就是在使用一个变量,函数体语句不会执行,也得不到函数的返回值。
② 返回值
1. 返回值是函数的计算结果,是函数调用表达式的值(函数名加括号就是函数调用表达式)
var res = fn(); // 把函数的返回值赋值给变量 res
fn() + 100; // 函数的返回值与 100 相加
console.log(fn()); // 输出函数的返回值
2. 通过 return 关键字定义函数的返回值
return 需写在函数体内,return 右边需写一个表达式(变量、直接量、带运算符的表达式), 表达式的值就是函数的返回值。
如果 return 右边是空的,函数没有返回值, 没有返回值函数调用表达式的值会自动得到 undefined
3. return 还可以结束函数的执行; 一旦执行到 return,后面的语句就不会执行了。
2.4 函数的参数
① 形参和实参
形参: 函数声明时设置的参数就是形参,相当于没有赋值的变量,形参的形式必须以变量名的形式给出。
实参: 调用函数时所给的参数就是实参,用于给形参赋值, 实参的形式可以是变量、直接量、表达式。
② 形参和实参的数量问题
标准情况下,设置了多少个形参,调用函数的时候就要给多少个实参。
如果实参数量比形参数量少,后面的形参会没有被赋值,自动得到 undefined。
如果实参数量比形参数量多,多余的实参就没有用了。
③ 形参的默认值(可选参数)
ES5 规范中设置默认值的方式:
function fn(name, age) {
// 如果 age 没有对应的实参, 设置一个默认值
if (age === undefined) {
age = 默认值;
}
}
ES6 规范中的设置默认值的方式:
funciton fn(name, age=默认值) {
}
总结:
1. 把具有默认值的参数称之为 可选参数
2. 可选参数必须放在必选参数的后面。 因为实参是按照顺序给形参赋值的。
④ arguments
1. arguments 只能在函数内使用, arguments 是系统定义好的变量,可以直接使用
2. arguments 是个伪数组对象,里面的成员是函数调用时传进来的实参; arguments 具有length 属性,可以通过索引获取到其中的每个成员。
3. arguments 是除了形参之外另外一种获取实参的方式。 形参只能获取固定数量的实参,arguments 可以获取所有的实参
4. 使用 arguments 可以定义可变参数数量的函数。
// 计算所有参数的和,返回结果
function sumFn() {
// 定义变量 记录和
var sum = 0;
// 遍历 arguments
for (var i = 0; i < arguments.length; i ++) {
sum += arguments[i];
}
// 返回结果
return sum;
}
2.5 作用域
① 变量的作用域
作用域: 作用域指的是变量的可作用范围; 变量根据作用域可以分为全局变量和局部变量。
全局变量: 在函数以外定义的变量就是全局变量,全局变量的作用域是全局。
局部变量: 在函数内定义的变量就是局部变量,局部变量的作用域是所在的函数。
总结:
1. 函数的形参是局部变量,作用域是所在的函数。
2. 函数名本质上是变量,所以函数本身也有作用域,由函数是在哪里声明的决定。
3. 如果在函数里面不使用var声明了变量,该变量是全局的(严格模式下不允许不使用var声明的变量的)
② 作用域链
什么是作用域链?
函数的里面还可以继续声明函数,函数的嵌套关系形成了作用域链
函数内可以使用本层作用域和上层作用域中的变量
作用域链描述变量查找的过程:
当使用某个变量的时候,先从本作用域中查找,如果找不到就去上层作用域查找,哪里找到哪里停止,找不到继续向上查找,直到全局作用域,如果仍然没有查找到该变量,报错。
变量的作用域只与函数声明的位置有关系,与函数在哪里调用无关!
2.6 变量提升
① 变量提升
1. 代码正式执行之前,会进行预解析,预解析的时候会把变量提升到本作用域的最前面(只创建了变量,却没有值)
2. 全局变量在整个代码正式执行之前就发生了提升
3. 局部变量在函数体语句执行之前进行提升
② 函数提升
1. 函数名本质上就是变量,所以函数也会提升, 也会提升到本作用域的最前面。
2. 如果是 function 关键字形式声明的函数,提升比较彻底,代码执行之前不但创建了函数名并且有值
3. 使用表达式方式声明的函数,提升规则与普通变量就没有差别
4. 函数提升与变量提升的区别:
① 函数提升更彻底
② 正式执行代码的时候,执行到变量声明语句会进行赋值操作, 执行到函数声明语句会跳过
2.7 匿名函数
1. 匿名函数就是没有名字的函数, 匿名函数就是函数这种数据类型的直接量表示
2. 匿名函数适合作为自调用函数和回调函数
2.8 自调用函数 (IIFE 立即执行的函数)
1. 自调用函数也称为立即执行的函数,指的是函数声明完就立即被调用
2. 自调用函数的主要作用是用来产生作用域,避免全局变量污染
// 匿名的自调用函数(立即执行的函数)
(function() {
console.log('大家好啊,我是一个可爱的匿名函数');
})();
console.log('');
// 有名字的自调用函数
(function fn() {
console.log('大家好,我是一个有名字的函数,我的名字叫 fn');
})();
// fn(); 报错
console.log('');
// 匿名的自调用函数 设置参数
(function(name, address) {
console.log('我叫'+name+', 住在'+address);
})('小乐', '桥洞里');
2.9 回调函数
① 什么是回调函数
具有以下三个条件的函数称为回调函数:
-
① 函数是我定义的
-
② 我没有直接调用该函数
-
③ 函数却执行了
注意: 回调函数大部分情况会作为其他函数(或方法)的参数。
② 回调函数的使用场景
1. 作为DOM事件的回调函数
2. 作为 Ajax 的回调函数
3. 作为定时器的回调函数
4. 作为框架的钩子函数
5. 其他需要一个函数作为参数的系统函数