1 定义
1.1 MDN解释
一般来说,一个函数是可以通过外部代码调用的一个“子程序”(或在递归的情况下由内部函数调用)。像程序本身一样,一个函数由称为函数体的一系列语句组成。值可以传递给一个函数,函数将返回一个值。
风安的理解就是一个工具,可以将代码块包裹在里面,并且对里面的代码进行一遍加工,再进行输出。
1.2 作用
1)函数可以减少代码量,消除冗余的代码
2)对代码进行模块化“加工”
3)封装代码,可以使函数内部的代码对外不可见
2 组成
- 函数名
- 函数体
- 参数
- 返回值
3 定义函数
- function字面量方式
function 函数名(参数) {
}
- 表达式方式
var 函数名 = function (参数) {
};
- Function构造函数方式
var 函数名 = new Function ('参数', '函数体') ;
4 调用函数
函数名加()就可以调用函数,并执行函数内的语句。
5 函数的返回值
- 函数名() 为函数调用表达式,表达式的值就是函数的返回值
- 函数内return 可以返回函数的返回值,可以是表达式、变量、直接量
- 函数体内没有return,则返回默认为undefied
6 函数的参数
6.1 形参和实参
形参:声明函数的时候,给的参数, 类似于变量名;在声明函数的时候,参数是没有值。
实参:调用函数是给的参数; 实参会按照顺序赋值给形参。
6.2 形参和实参的数量问题
- 一般来书形参数量和实参数量应该相等
- 如果实参大于形参数量,会忽略多出来的参数
- 如果实参小于形参数量,形参取值时为undefined
6.3 形参的默认值
function demo(a, b=默认值) {
}
有默认值的形参一定要放后面!
6.4 arguments
- arguments 只能在函数中使用
- arguments 是一个类数组对象,并有数组的特性
- arguments 能获取所有实参
因此,可以定义可变参数数量的函数:如计算所有参数和、取参数中的最大值、求所有参数的平均数
// 求参数的和
function fn() {
let sum = 0;
for (let i = 0; i < arguments.length; i++) {
sum += arguments[i];
}
return sum;
}
let sum = fn(12, 13, 14, 23, 35, 16);
console.log(sum);
// 求参数的成积
function product() {
let res = 1;
for (let i = 0; i < arguments.length; i++) {
res *= arguments[i];
}
return res;
}
let result = product(12, 13, 14, 23, 35, 16);
console.log(result);
// 求参数的平均数
function average() {
let add = 0;
let averNum = 0;
for (let i = 0; i < arguments.length; i++) {
add += arguments[i];
averNum = parseInt(add / arguments.length);
}
return averNum;
}
let result1 = average(12, 13, 14, 23, 35, 16);
console.log(result1);
function average1() { // 调用上面的求和结果
return sum / arguments.length;
}
let result2 = average1(12, 13, 14, 23, 35, 16);
console.log(result2);
// 求参数的最大、最小值
function num() {
let a = arguments[0]; //假设最大值a我arguments的第一个
let b = arguments[0];
for (let i = 0; i < arguments.length; i++) {
if (a < arguments[i]) {
a = arguments[i];
}
}
return a;
}
let max = num(12, 13, 14, 23, 35, 16);
console.log(max);
7 函数的嵌套
- 冒泡排序 arrSolt函数中嵌套一个exchange函数
// 冒泡排序 将数组从小到大排列
function arrSolt(arr) {
// 遍历数组
for (var i = 0; i < arr.length; i++) {
// 开始比较大小 将最大的元素放到最后
for (var j = 0; j < arr.length - 1 - i; j++) {
// 如果前面的元素比后面的小,就交换位置
if (arr[j + 1] < arr[j]) {
exchange();
}
}
}
return arr;
// 交换函数 设置第三个变量用于交换时存放
function exchange() {
let temp = arr[j + 1];
arr[j + 1] = arr[j];
arr[j] = temp;
}
}
console.log(arrSolt(arr = [1, 13, 0, 999, 38, 620, 43, 186, 80, 29, 290]));
8 作用域
8.1 变量的作用域
作用域是变量的可作用范围,变量只有在自己的作用域下才会生效。
8.2 作用域分类
局部作用域:函数内部定义的变量和形参的作用域就是局本部作用域,这样的变量为局部变量。
全局作用域:在函数外面定义的变量的作用域是全局作用域,这样的变量为全局变量。
块级作用域:在代码块中定义的变量的作用域为块级作用域,这样的变量为块级变量,ES6及以上才支持
8.3 作用域链
8.3.1 定义
- 函数的层层嵌套就形成了作用域链
- 作用域链是程序执行过程中寻找变量的过程
8.3.2 通过作用域链查找变量的过程
- 先在当前作用域查找,如果找不到就去上级作用域查找,直到找到全局作用域中去
- 往上寻找的过程中,找到了就会停止查找,并使用该变量
- 寻找的过程中没有找到,就会报错提示**is not defined
变量的作用域只与函数声明的位置有关,与函数调用的位置无关!
8.4 作用域面试题
var num=10;
function fun(){
var num=20;
fun2();
}
function fun2(){
console.log(num); // 10
}
fun();
9 变量提升和函数提升
9.1 变量提升
- JS会将变量的声明提升到本作用域的最前面。
9.2 函数提升
- JS会把函数连声明带值提升到本作用域的最前面,函数可以在函数声明之前调用。
- 只有字面量方式(function关键字方式)声明的函数才能进行函数提升,表达式方式和构造函数方式声明的函数只能提升声明没有值,这一点和变量提升相同。
- 函数提升的权重比变量提升高(前提是变量和函数名相同的情况)。
9.3 预解析
程序在代码执行前会先进行预解析
- 预解析先解析函数声明定义的函数,整体会被提升
- 再解析带var 声明的变量
- 函数重名会覆盖,变量重名会忽略
- 变量若不带var关键字,该变量是不会进行预解析的
- 表达式和构造函数方式定义的函数也是当作变量进行解析
9.4 变量提升面试题
// 题一
alert(a);
a = 0;
// a is not defined
// 题二
alert(a);
var a = '我是变量';
function a() { alert('我是函数') }
alert(a);
// undefined
// 0
// 题三
alert(a);
var a = '我是变量';
function a() { alert('我是函数') }
alert(a);
// function a() { alert('我是函数') }
// 我是变量
// 题四
alert(a);
a++;
alert(a);
var a = '我是变量';
function a() { alert('我是函数') }
alert(a)
// function a() { alert('我是函数') }
// NaN
// 我是变量
// 题五
alert(a);
var a = 0;
alert(a);
function fn(){
alert(a);
var a = 1;
alert(a);
}
fn()
alert(a);
// 执行顺序 第一个alert 第二个alert fn函数内部的代码 第三个alert
// undefined
// 0
// undefined
// 1
// 0
10 子调用函数 IIFE
10.1 匿名函数
function() {
// 匿名函数
}
匿名函数声明后要立即调用,否则没有意义
10.2 自动调用函数
函数声明完立即调用,称为自调用函数,英文简称IIFE——Immediately Invoked Function Expression
// 自调用函数
(function() {
console.log('我被调用了');
})();
// 带参数的自调用函数
(function(a, b) {
console.log(a + b);
})(10,20);
两个连续的自调用函数之间必须加分号,不然会报错
10.3自调用函数的作用
- 减少全局变量的使用,把自己代码或每个特效的代码写到一个自调用函数中,防止全局变量污染
- 隐藏内部代码暴露接口,实现模块化开发
11 回调函数
11.1 解释
函数全部满足以下条件就是回调函数:
- 函数是我定义的
- 我没有调用
- 函数被执行了
11.2 用途
- 事件函数
- 定时器函数
- Ajax的回调函数
- 生命周期钩子函数
很多时候匿名函数作为回调函数
11.3 举例
function fn(callback) {
callback();
}
// 调用fn,传匿名函数进去
fn(function() {
console.log('回调函数被执行了');
});
function progress(num1, num2, call) {
call(num1, num2);
}
process(100, 200, function(a, b) {console.log(a + b)});
12 递归函数
12.1 概念
一个函数的内部如果再次调用了自己,则称为是函数的递归调用,这样的函数为递归函数
12.2 形成递归函数的条件
- 必须有明确的结束条件
- 必须有趋近于结束条件的趋势
// 使用递归求1~100的和
function fun(n) {
if (n == 1) return 1;
return n + fun(n - 1);
}
var result = fun(100);
console.log(result); // 5050
12.3 缺点
- 递归容易导致内存泄露
- 函数递归的效率比较低
12.4 应用
- 删除文件夹中的内容(操作系统的原始接口只能删除文件和空文件夹)
- 复制文件夹里面的内容
- 剪切文件夹以及里面的内容