函数的概念
函数:函数就是封装了一段可被重复调用执行的代码块,通过此代码块可以实现大量代码的重复使用
函数的封装:把一个或者多个功能通过函数的方式封装起来,对外只提供一个简单的函数接口
具名函数:
function 函数名(形式参数1,形式参数2){
语句
return 返回值
}
funciton fun(x,y){
return x+y
}
匿名函数:上面的函数名去掉成为匿名函数
let a = function(x,y){
return x+y
}
箭头函数
箭头函数定义
ES6中新增的定义函数方式,用来简化函数定义语法的
let fn = () => {
console.log(123);
}
fn();
如果形参只有一个,可以省略小括号
let fn = x => x*x
如果函数体中只有一句代码,且代码的执行结果就是返回值,可以省略大括号
let fn = (x,y) => x+y // 圆括号不能省略
let fn = (x,y) => {return x+y} // 花括号不能省略
let fn = (x,y) => ({name: x, age: y}) // 直接返回对象会报错,需要加个小括号
箭头函数不绑定this关键字
箭头函数不绑定this关键字,箭头函数中的this,指向的是函数定义位置的上下文this
function fn() {
console.log(this);
return () => {
console.log(this);
};
}
const obj = { name: "张三" };
const resFn = fn.call(obj);
resFn();
函数调用
let fn() => console.log('hi')
fn; // fn没执行,不会输出结果
let fn() => console.log('hi')
fn(); // 打印出hi,有圆括号才是调用
let fn() => console.log('hi')
let fn2 = fn
fn2();
结果:
- fn 保存了匿名函数的地址
- 这个地址被复制给了fn2
- fn2调用了匿名函数
- fn和fn2都是匿名函数的引用而已
- 真正的函数既不是fn也不是fn2
函数的使用
1. 声明函数
- function声明函数关键字,全部小写
- 函数是做某件事情,函数名一般是动词,sayHi
- 函数不调用自己不执行
function sayHi() {
return hi~;
}
2. 调用函数
函数名();
sayHi();
3. 函数的调用时机
当一个函数被写好以后,并不会马上执行,而是等待被调用,所以什么时候被调用就决定了函数的输出结果是什么 eg1:
let a = 1;
function fn() {
console.log(a);
}
fn(); // 这时候会在控制台打印出数字1
eg2:
let a = 1;
function fn() {
console.log(a);
}
a = 2;
fn(); // 这时候会在控制台打印出数字2,因为fn()的调用时间在a=2之后,这时候 a 已经被重新赋值为 2 了,所以输出结果是 2,如果这里fn()调用在a=2之前,则会输出 1
eg3:
let a = 1;
function fn() {
setTimeout(() => {
console.log(a);
}, 0);
}
fn();
a = 2; // 这个代码会输出2,因为 setTimeout()这个函数的意思是等一会执行,所以当你调用 fn()的时候并不会马上输出 a,而是继续执行a=2,然后才执行console.log(a)
eg4:
let i = 0;
for (i = 0; i < 6; i++) {
setTimeout(() => {
console.log(i);
}, 0);
}
// 以上代码的输出结果是 6 个 6,是不是跟你预期的 0-5 有出入 原因是setTimeout()函数回调属于异步任务,会出现在宏任务队列中,被压到了任务队列的最后,在这段代码应该是for循环这个同步任务执行完成后才会轮到它 简单来说当循环执行到setTimeout()的时候并不会马上打印出 i,而是会记下一个事件,”我等下要把i打一次“,然后会继续执行循环, 当 for 循环结束的时候,这时候 i 的值是 6,然后setTimeout()开始执行,因为刚才循环了 6 次,所以会将这个语句也执行 6 次,这时候会把i的值打印出来,但是这时候i=6,所以他就连续打了 6 个 6,是不是 666666。 但是这种性质其实对新人非常不友好,因为他是反直觉的,所以在最新版的 JS 语法中,做了一个微调
eg5:
for (let i = 0; i < 6; i++) {
setTimeout(() => {
console.log(i);
}, 0);
}
// 将let i=0放到 for 的条件里之后,情况发生了改变,这时候的输出结果就是一般人认为的,0-5 的输出 6 个数字
其实这个问题还有另一种解决方法,那就是立即执行函数和闭包
for (let i = 0; i < 6; i++) {
(function fn(i){setTimeout(() => {
console.log(i);
}, 0);})(i)
} // 在闭包函数内部形成局部作用域,不受外界变量变化的影响
4. 函数的作用域
每个函数都会默认创建一个作用域 eg1:
function fn() {
let a = 1;
}
fn();
console.log(a); // a不存在,访问不到作用域里面的a
- 闭包:如果一个函数用到了外部的变量,那么这个函数加这个变量就叫做闭包
function f1() {
let a = 1;
function f2() {
let a = 2;
function f3() {
console.log(a);
}
a = 22;
f3();
}
console.log(a);
a = 100;
f2();
}
f1();
a和f3组成了闭包
函数的参数
- 形参:形式上的参数,函数定义的时候,传递参数,当前并不知道是什么
function getSum(num1, num2) { // num1和num2为形参
return num1 + num2;
}
console.log(getSum(1, 2));
- 实参:实际的参数,函数调用的时候传递的参数,实参是传给形参的
function getSum(num1, num2) {
return num1 + num2;
}
console.log(getSum(1, 2)); // 1,2 为实参
函数的参数可以有,也可以没有
- 如果实参的个数和形参的个数一致,则正常输出结果
getSum(1,2); // 3
- 如果实参的个数多余形参的个数,会取到形参的个数
getSum(1,2,3); // 3
- 如果实参的个数小于形参,多余的形参定义为 undefined,最终的结果为NaN
getSum(1); // NaN
建议形参个数和实参个数匹配
函数的返回值
通过return将结果返回给函数的调用者
函数返回值格式
function 函数名(){
return 需要返回的结果
}
函数名();
求一组数组中的最大值
function getArrayMax(arr) {
let max = arr[0];
for (let i = 1; i <= arr.length; i++) {
if (arr[i] > max) {
max = arr[i];
}
}
return max;
}
let re = getArrayMax([1, 2, 3]);
console.log(re);
函数返回值注意事项
- return 终止函数,后面的代码不会执行
- return 只能返回一个值,如果用逗号隔开多个值,以最后一个为准。如果要输出多个值,可以借助数组来实现
- 函数都有返回值,且只有函数有返回值,函数如果没有return,返回undefined
arguments的使用
当我们不确定有多少个参数传递的时候,可以用arguments来获取,arguments是当前函数的内置对象,arguments存储了传递的所有实参
function fun() {
console.log(arguments);
}
fun(1, 2, 3);
//
Arguments(3) [1, 2, 3, callee: ƒ, Symbol(Symbol.iterator): ƒ]
0: 1
1: 2
2: 3
callee: ƒ fun()
length: 3
Symbol(Symbol.iterator): ƒ values()
__proto__: Object
arguments的展示形式是一个伪数组,因此可以进行遍历。伪数组具有以下特点:
- 具有数组的length属性
- 按索引方式存储数据
- 不具有数组的push,pop等方法
函数案例
利用函数求任意个数的最大值
function getMax() {
let max = arguments[0];
for (let i = 1; i < arguments.length; i++) {
if (arguments[i] > max) {
max = arguments[i];
}
}
return max;
}
console.log(getMax(1, 2, 3,4,5));
利用函数翻转任意数组
function reverse(arr) {
let newArr = [];
for (let i = arr.length - 1; i >= 0; i--) {
newArr[newArr.length] = arr[i];
}
return newArr;
}
let arr1 = reverse([1, 2, 3, 4, 5]);
console.log(arr1);
利用函数封装对数组进行排序-冒泡排序
function sort(arr) {
for (let i = 0; i < arr.length - 1; i++) {
for (let j = 0; j < arr.length - i - 1; j++) {
if (arr[j] > arr[j + 1]) {
let temp = arr[j];
arr[j] = arr[j + 1];
arr[j + 1] = temp;
}
}
}
return arr;
}
let arr1 = sort([1, 3, 6, 4, 8, 5]);
console.log(arr1);
函数之间可以相互调用
输入年份,输出当前年份的2月份天数
function backDay() {
let year = prompt("请输入年份:");
if (isRunYear(year)) {
alert("当前年份是闰年2月份有29天");
} else {
alert("当前年份是平年2月份有28天");
}
}
backDay();
function isRunYear(year) {
//如果是闰年返回true,不是则返回false。
let flag = false;
if ((year % 4 == 0 && year % 100 != 0) || year % 400 == 0) {
flag = true;
}
return flag;
}